本文将介绍在android平台下如何实现多线程下载,大家都知道,android平台使用java做为开发语言,所以java中支持的多线程下载方式在android平台下都支持,其中主要有两种方式可以实现多线程下载。
一种方式是使用很多个线程分别下载文件的不同部分,最后把所有下载完的文件合并成一个文件。另一种方式是使用java为我们提供的RandomAccessFile类实现多线程的下载。
从性能上分析,第二种方式的存取速度会慢一些,但开发起来较为容易,不需要进行合并文件等操作。本文将使用第二种方式来实现多线程下载,最终效果如下图所示:
使用图形界面来获取需要下载的内容,并实时更新下载进度条,代码如下所示:
import java.io.File;
import java.net.URL;
import java.net.URLConnection;
import android.app.Activity;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.TextView;
/**
* Copyright (C) 2010 ideasandroid
* 演示android多线程下载
* 欢迎访问http://www.ideasandroid.com
* 让程序开发不再那么神秘
*/
public class FileDownloadDemo extends Activity { private EditText downloadUrl;
private EditText downloadFileName;
private EditText downloadThreadNum;
private Button downloadBt;
private ProgressBar downloadProgressBar;
private TextView progressMessage;
private int downloadedSize = ;
private int fileSize = ; @Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main); downloadUrl = (EditText) findViewById(R.id.downloadUrl);
downloadFileName = (EditText) findViewById(R.id.downloadFileName);
downloadThreadNum = (EditText) findViewById(R.id.downloadThreadNum);
progressMessage = (TextView) findViewById(R.id.progressMessage);
downloadBt = (Button) findViewById(R.id.downloadBt);
downloadProgressBar = (ProgressBar) findViewById(R.id.downloadProgressBar);
downloadProgressBar.setVisibility(View.VISIBLE);
downloadProgressBar.setMax();
downloadProgressBar.setProgress();
downloadBt.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
download();
}
});
} private void download() {
// 获取SD卡目录
String dowloadDir = Environment.getExternalStorageDirectory()
+ "/ideasdownload/";
File file = new File(dowloadDir);
//创建下载目录
if (!file.exists()) {
file.mkdirs();
} //读取下载线程数,如果为空,则单线程下载
int downloadTN = Integer.valueOf("".equals(downloadThreadNum.getText()
.toString()) ? "" : downloadThreadNum.getText().toString());
//如果下载文件名为空则获取Url尾为文件名
int fileNameStart = downloadUrl.getText().toString().lastIndexOf("/");
String fileName = "".equals(downloadFileName.getText().toString()) ? downloadUrl
.getText().toString().substring(fileNameStart)
: downloadFileName.getText().toString();
//开始下载前把下载按钮设置为不可用
downloadBt.setClickable(false);
//进度条设为0
downloadProgressBar.setProgress();
//启动文件下载线程
new downloadTask(downloadUrl.getText().toString(), Integer
.valueOf(downloadTN), dowloadDir + fileName).start();
} Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
//当收到更新视图消息时,计算已完成下载百分比,同时更新进度条信息
int progress = (Double.valueOf((downloadedSize * 1.0 / fileSize * ))).intValue();
if (progress == ) {
downloadBt.setClickable(true);
progressMessage.setText("下载完成!");
} else {
progressMessage.setText("当前进度:" + progress + "%");
}
downloadProgressBar.setProgress(progress);
} }; /**
* @author ideasandroid
* 主下载线程
*/
public class downloadTask extends Thread {
private int blockSize, downloadSizeMore;
private int threadNum = ;
String urlStr, threadNo, fileName; public downloadTask(String urlStr, int threadNum, String fileName) {
this.urlStr = urlStr;
this.threadNum = threadNum;
this.fileName = fileName;
} @Override
public void run() {
FileDownloadThread[] fds = new FileDownloadThread[threadNum];
try {
URL url = new URL(urlStr);
URLConnection conn = url.openConnection();
//获取下载文件的总大小
fileSize = conn.getContentLength();
//计算每个线程要下载的数据量
blockSize = fileSize / threadNum;
// 解决整除后百分比计算误差
downloadSizeMore = (fileSize % threadNum);
File file = new File(fileName);
for (int i = ; i < threadNum; i++) {
//启动线程,分别下载自己需要下载的部分
FileDownloadThread fdt = new FileDownloadThread(url, file,
i * blockSize, (i + ) * blockSize - );
fdt.setName("Thread" + i);
fdt.start();
fds[i] = fdt;
}
boolean finished = false;
while (!finished) {
// 先把整除的余数搞定
downloadedSize = downloadSizeMore;
finished = true;
for (int i = ; i < fds.length; i++) {
downloadedSize += fds[i].getDownloadSize();
if (!fds[i].isFinished()) {
finished = false;
}
}
//通知handler去更新视图组件
handler.sendEmptyMessage();
//休息1秒后再读取下载进度
sleep();
}
} catch (Exception e) { } }
}
}
import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.URL;
import java.net.URLConnection; import android.util.Log;
/**
* Copyright (C) 2010 ideasandroid
* 演示android多线程下载
* 欢迎访问http://www.ideasandroid.com
* 让程序开发不再那么神秘
*
* 单个下载线程
*/
public class FileDownloadThread extends Thread{
private static final int BUFFER_SIZE=;
private URL url;
private File file;
private int startPosition;
private int endPosition;
private int curPosition;
//用于标识当前线程是否下载完成
private boolean finished=false;
private int downloadSize=;
public FileDownloadThread(URL url,File file,int startPosition,int endPosition){
this.url=url;
this.file=file;
this.startPosition=startPosition;
this.curPosition=startPosition;
this.endPosition=endPosition;
}
@Override
public void run() {
BufferedInputStream bis = null;
RandomAccessFile fos = null;
byte[] buf = new byte[BUFFER_SIZE];
URLConnection con = null;
try {
con = url.openConnection();
con.setAllowUserInteraction(true);
//设置当前线程下载的起点,终点
con.setRequestProperty("Range", "bytes=" + startPosition + "-" + endPosition);
//使用java中的RandomAccessFile 对文件进行随机读写操作
fos = new RandomAccessFile(file, "rw");
//设置开始写文件的位置
fos.seek(startPosition);
bis = new BufferedInputStream(con.getInputStream());
//开始循环以流的形式读写文件
while (curPosition < endPosition) {
int len = bis.read(buf, , BUFFER_SIZE);
if (len == -) {
break;
}
fos.write(buf, , len);
curPosition = curPosition + len;
if (curPosition > endPosition) {
downloadSize+=len - (curPosition - endPosition) + ;
} else {
downloadSize+=len;
}
}
//下载完成设为true
this.finished = true;
bis.close();
fos.close();
} catch (IOException e) {
Log.d(getName() +" Error:", e.getMessage());
}
} public boolean isFinished(){
return finished;
} public int getDownloadSize() {
return downloadSize;
}
}
Andorid多线程断点续传下载器:http://www.jcodecraeer.com/a/opensource/2015/0602/2978.html
Android文件下载(实现断点续传)的更多相关文章
-
Android文件下载之进度检测
近期因为项目的需要,研究了一下Android文件下载进度显示的功能实现,接下来就和大家一起分享学习一下,希望对广大初学者有帮助. 先上效果图: 上方的蓝色进度条,会根据文件下载量的百分比进行加载,中部 ...
-
【SFTP】使用Jsch实现Sftp文件下载-支持断点续传和进程监控
参考上篇文章: <[SFTP]使用Jsch实现Sftp文件下载-支持断点续传和进程监控>:http://www.cnblogs.com/ssslinppp/p/6248763.html ...
-
iOS开发之网络编程--4、NSURLSessionDataTask实现文件下载(离线断点续传下载) <;进度值显示优化>;
前言:根据前篇<iOS开发之网络编程--2.NSURLSessionDownloadTask文件下载>或者<iOS开发之网络编程--3.NSURLSessionDataTask实现文 ...
-
Android网络多线程断点续传下载
本示例介绍在Android平台下通过HTTP协议实现断点续传下载. 我们编写的是Andorid的HTTP协议多线程断点下载应用程序.直接使用单线程下载HTTP文件对我们来说是一件非常简单的事.那么,多 ...
-
android 多线程下载 断点续传
来源:网易云课堂Android极客班第八次作业练习 练习内容: 多线程 asyncTask handler 多线程下载的原理 首先获取到目标文件的大小,然后在磁盘上申请一块空间用于保存目标文件,接着把 ...
-
iOS开发之网络编程--3、NSURLSessionDataTask实现文件下载(离线断点续传下载)
前言:使用NSURLSessionDownloadTask满足不这个需要离线断点续传的下载需求,所以这里就需要使用NSURLSessionDataTask的代理方法来处理下载大文件,并且实现离线断点续 ...
-
实现android支持多线程断点续传下载器功能
多线程断点下载流程图: 多线程断点续传下载原理介绍: 在下载的时候多个线程并发可以占用服务器端更多资源,从而加快下载速度手机端下载数据时难免会出现无信号断线.电量不足等情况,所以需要断点续传功能根据下 ...
-
Android开发多线程断点续传下载器
使用多线程断点续传下载器在下载的时候多个线程并发可以占用服务器端更多资源,从而加快下载速度,在下载过程中记录每个线程已拷贝数据的数量,如果下载中断,比如无信号断线.电量不足等情况下,这就需要使用到断点 ...
-
Android Http 与断点续传
HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestM ...
随机推荐
-
C和指针 第十六章 标准函数库 本地跳转setjmp.h
setjmp和longjmp提供一种类似goto语句的机制,但它的作用域不局限于同一个函数的作用域之内.这些函数可以用于深层次的嵌套函数调用链. int setjmp(jmp_buf state); ...
-
ELK笔记
ELK笔记 ELKStack高级实战培训http://files.cnblogs.com/files/MYSQLZOUQI/ELKStack%E9%AB%98%E7%BA%A7%E5%AE%9E%E6 ...
-
PHP基础知识之————php5-cli 的安装以及phpredis的安装
在系统安装完后最好执行下列命令更新下软件 sudo apt-get update 安装php5-cli apt-get install php5-cli 下载phpredis wget https:/ ...
-
java---Swing界面开发总结
一.java的图形界面 1.awt java.awt jdk1.4之前推出的图形界面,用c/c++编写,跨平台性不好 2.swing javax.swing jdk1.4时推出的图形界面,跨平 ...
-
Class.forName(";ClassName";)与ClassName.class的区别
引发问题的来源:最近在看比较深入的JVM相关的书,不得不感慨,JVM确实是比较深奥,很多地方难以理解不说,在网上还找不到什么资料,发现一个左思右想都想不明白的问题上网来搜索,结果基本上都是从书上cop ...
-
C++定义错误码类
我们平时有这样的需求,可能是C用户的*惯了,在底层的组件中更喜欢用返回错误码的形式来告知用户函数的调用状态,一般来说,简单用#define 一个宏来包装下返回值. #define ERR_SYSTE ...
-
(转)通过注册表修改VC6.0的字体
出处:http://www.cnblogs.com/PocketZ 在VC6.0下更改字体,我们一般通过菜单-Tools-Options-Format来更改 但在我的win7 64位系统下这一选项下的 ...
-
mybatis之@Select、@Insert、@Delete、@Param
之前学习的时候,看到别人在使用mybatis时,用到@Select.@Insert.@Delete.@Param这几个注解,故楼主研究了一下,在这里与大家分享 当使用这几个注解的时候,可以省去写Map ...
-
全文搜索引擎 Elasticsearch (二) 使用场景
1.场景—:使用Elasticsearch作为主要的后端 传统项目中,搜索引擎是部署在成熟的数据存储的顶部,以提供快速且相关的搜索能力.这是因为早期的搜索引擎不能提供耐用的存储或其他经常需要的功能 ...
-
Derby设置密码教程
方法一: 配置derby.propertites文件: 文件内容: derby.connection.requireAuthentication=truederby.authentication ...