您的位置:首页 > 移动开发 > Android开发

android多线程下载

2016-12-29 14:55 218 查看
文章的开头奉送上代码,已方便大家对照学习。(因为电脑卡的原因,代码是用eclipse编写,给大家带来不便还请见谅。)

1 前言

1.1多线程下载

APP更新的时候如果单线程下载的话相对来说是比较慢的。但是如果能把APK分成几段,分别用线程下载的话,就会快很多。接下来就来讲解一下多线程下载。

1.2断点续传

多线程下载如果理解了,断点续传就好做多了。用数据库保存相关断点,继续下载就行,这里就不介绍了。

2 实现思路

2.1 用到的核心知识点

1.RandomAccessFile 随机访问文件的读写。
2.BufferedInputStream缓冲流的读写。
3.URLConnection.setRequestProperty("Range", "bytes=" + startPos + "-" + endPos);设置下载的起至段。


2.2 实现思路

1.启动服务service,在服务中启动线程请求地址,获取下载内容大小。
2.根据下载内容的大小分成3段,分成3个线程分别下载。
3.在每个线程中分成设置下载位置进行分别下载。
4.将下载下来的流利用RandomAccessFile写入文件。


总体的实现思路就是这样的。下面来讲一下编码实现。

3 代码实现

3.1 启动服务

Intent intent = new Intent("com.action.downloadservice");
startService(intent);


3.2 DownLoadService类的代码

/**
* @author 作者 YYD
* @version 创建时间:2016年12月28日 下午1:59:07
* @function 未添加
*/
@SuppressLint("NewApi")
public class DownLoadService extends Service implements Callback {
private Handler handler = new Handler(this);
private int allFileLength = 0;//软件总大小
private int currentFileLenght = 0;//当前下载的数量
private long preDownLoadTime = 0;//下载前的时间记录
private int ThreadNum = 3;//线程数
@Override
public void onCreate() {
super.onCreate();
preDownLoadTime = System.currentTimeMillis();
mulitDownload();
}

@Override
public IBinder onBind(Intent intent) {
return null;
}
/**
*这个是handler用于计算3个线程下在的总进度,
*如果(下载的大小==总内容长度)说明下载完了。
*/
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case 1000:
int num = msg.arg1;
currentFileLenght += num;
if(currentFileLenght == allFileLength){
Log.d("refreshlistview", "下载完毕!"+"消耗时间:"+((System.currentTimeMillis() - preDownLoadTime)/1000));
}
break;
}
return false;
}

/**
* 多线程下载
* 这个是核心代码,思路如下:
* 1.线程请求网络,获取内容长度。
* 2.根据下载内容长度,分3段,分3个线程分别下载。
* 比如:现在长度是1534。
*      第一段是:0 ~ 1500/3*1 - 1
*      第二段是:1500/3*1 ~ 1500/3*2 - 1
*      第三段是:1500/3*2 ~ 1534
* 这就是for循环中的逻辑,用for循环分别启动3个线程来下载这3段。
*/
private void mulitDownload() {
new Thread(new Runnable() {
@Override
public void run() {
try {
URL url = new URL(UrlManager.url);
URLConnection connection = url.openConnection();
allFileLength = connection.getContentLength();// 获取内容的长度
File file = new File(
Environment.getExternalStorageDirectory(),
"gaoyipin.apk");
if (!file.exists()) {
boolean isCreate = file.createNewFile();
}
DownLoadThread[] threads = new DownLoadThread[ThreadNum];// 用于管理下载线程
for (int i = 0; i < ThreadNum; i++) {
DownLoadThread thread = null;
if (i == (ThreadNum -1)) {
thread = new DownLoadThread(allFileLength / ThreadNum * (ThreadNum-1), allFileLength,
file, handler, i);
} else {
thread = new DownLoadThread(allFileLength / ThreadNum * i, allFileLength
/ ThreadNum * (i + 1) - 1, file, handler, i);
}
thread.start();
threads[i] = thread;
}

} catch (Exception e) {
Log.d("refreshlistview", "下载前异常");
}
}
}).start();
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d("refreshlistview", "服务停止");
}
}


3.3 DownLoadThread类的代码

/**
* @author 作者 YYD
* @version 创建时间:2016年12月28日 下午2:06:00
* @function 未添加
*/
public class DownLoadThread extends Thread {
private int process = 0;
private int startPos = 0;//开始下载位置
private int endPos = 0;//结尾下载的进度
private File downLoadFile;//要下载的文件
private Handler handler ;
public DownLoadThread(int startPos, int endPos, File downLoadFile,Handler handler,int threadId) {
super(""+threadId);
this.startPos = startPos;
this.endPos = endPos;
this.downLoadFile = downLoadFile;
this.handler = handler;
Log.d("refreshlistview", "startPos:"+startPos+"  endPos: "+endPos);
}

@Override
public void run() {
super.run();
try {
URL url = new URL(UrlManager.url);
URLConnection connection = url.openConnection();
connection.setAllowUserInteraction(true);
connection.setRequestProperty("Range", "bytes=" + startPos + "-" + endPos);
InputStream is = connection.getInputStream();
RandomAccessFile raf = new RandomAccessFile(
downLoadFile.getAbsolutePath(), "rw");// 随机访问文件的读写,第一个参数是文件的路径
raf.seek(startPos);
BufferedInputStream bis = new BufferedInputStream(is);
byte[] buffer = new byte[2048];
int writeNum = -1;
while ((writeNum = bis.read(buffer))> 0) {
raf.write(buffer,0,writeNum);

Message msg = new Message();
msg.what = 1000;
msg.arg1 = writeNum;
handler.sendMessage(msg);
process += writeNum;

}
Log.d("refreshlistview", "线程"+getName()+"下载完毕");
raf.close();
bis.close();
is.close();
} catch (Exception e) {
Log.d("refreshlistview", "线程"+getName()+"下载异常:"+e.toString());
}
}
}


4 结尾

文章的最后奉送上代码,方便大家对照学习。

好了就讲到这里吧,在技术上我依旧是个小渣渣,加油,勉励自己!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息