本篇文章主要介绍了"AsyncTask实现多任务多线程断点续传下载",主要涉及到文件下载,线程池,博客,Exception,断点续传方面的内容,对于Android开发感兴趣的同学可以参考一下:
这篇博客是AsyncTask下载系列的最后一篇文章,前面写了关于断点续传的和多线程下载的博客,这篇是在前两篇的基础上面实现的,有兴趣的可以去看下。 一、Asy...
这篇博客是AsyncTask下载系列的最后一篇文章,前面写了关于断点续传的和多线程下载的博客,这篇是在前两篇的基础上面实现的,有兴趣的可以去看下。
一、AsyncTask实现断点续传
二、AsyncTask实现多线程断点续传
这里模拟应用市场app下载实现了一个Demo,因为只有一个界面,所以没有将下载放到Service中,而是直接在Activity中创建。在正式的项目中,下载都是放到Service中,然后通过BroadCast通知界面更新进度。
上代码之前,先看下demo的运行效果图吧。

下面我们看代码,这里每一个文件的下载定义一个Downloador来管理下载该文件的所有线程(暂停、下载等)。Downloador创建3个DownloadTask(这里定义每个文件分配3个线程下载)来下载该文件特定的起止位置。这里要通过文件的大小来计算每个线程所下载的起止位置,详细可以参考《AsyncTask实现多线程断点续传》。
1、Downloador类
1package com.bbk.lling.multitaskdownload.downloador;
2 3import android.content.Context;
4import android.content.Intent;
5import android.os.AsyncTask;
6import android.os.Bundle;
7import android.os.Handler;
8import android.os.Message;
9import android.text.TextUtils;
10import android.util.Log;
11import android.widget.Toast;
12 13import com.bbk.lling.multitaskdownload.beans.AppContent;
14import com.bbk.lling.multitaskdownload.beans.DownloadInfo;
15import com.bbk.lling.multitaskdownload.db.DownloadFileDAO;
16import com.bbk.lling.multitaskdownload.db.DownloadInfoDAO;
17import com.bbk.lling.multitaskdownload.utils.DownloadUtils;
18 19import org.apache.http.HttpResponse;
20import org.apache.http.client.HttpClient;
21import org.apache.http.client.methods.HttpGet;
22import org.apache.http.impl.client.DefaultHttpClient;
23 24import java.util.ArrayList;
25import java.util.List;
26import java.util.concurrent.Executor;
27import java.util.concurrent.Executors;
28 29/** 30 * @Class: Downloador
31 * @Description: 任务下载器
32 * @author: lling(www.cnblogs.com/liuling)
33 * @Date: 2015/10/13
34*/ 35publicclass Downloador {
36publicstaticfinal String TAG = "Downloador";
37privatestaticfinalint THREAD_POOL_SIZE = 9; //线程池大小为9 38privatestaticfinalint THREAD_NUM = 3; //每个文件3个线程下载 39privatestaticfinalint GET_LENGTH_SUCCESS = 1;
40publicstaticfinal Executor THREAD_POOL_EXECUTOR = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
41 42private List tasks;
43private InnerHandler handler = new InnerHandler();
44 45private AppContent appContent; //待下载的应用 46privatelong downloadLength; //下载过程中记录已下载大小 47privatelong fileLength;
48private Context context;
49private String downloadPath;
50 51public Downloador(Context context, AppContent appContent) {
52this.context = context;
53this.appContent = appContent;
54this.downloadPath = DownloadUtils.getDownloadPath();
55 }
56 57/** 58 * 开始下载
59*/ 60publicvoid download() {
61if(TextUtils.isEmpty(downloadPath)) {
62 Toast.makeText(context, "未找到SD卡", Toast.LENGTH_SHORT).show();
63return;
64 }
65if(appContent == null) {
66thrownew IllegalArgumentException("download content can not be null");
67 }
68new Thread() {
69 @Override
70publicvoid run() {
71//获取文件大小 72 HttpClient client = new DefaultHttpClient();
73 HttpGet request = new HttpGet(appContent.getUrl());
74 HttpResponse response = null;
75try {
76 response = client.execute(request);
77 fileLength = response.getEntity().getContentLength();
78 } catch (Exception e) {
79 Log.e(TAG, e.getMessage());
80 } finally {
81if (request != null) {
82 request.abort();
83 }
84 }
85//计算出该文件已经下载的总长度 86 List lists = DownloadInfoDAO.getInstance(context.getApplicationContext())
87 .getDownloadInfosByUrl(appContent.getUrl());
88for (DownloadInfo info : lists) {
89 downloadLength += info.getDownloadLength();
90 }
91 92//插入文件下载记录到数据库 93 DownloadFileDAO.getInstance(context.getApplicationContext()).insertDownloadFile(appContent);
94 Message.obtain(handler, GET_LENGTH_SUCCESS).sendToTarget();
95 }
96 }.start();
97 }
98 99/**100 * 开始创建AsyncTask下载
101*/102privatevoid beginDownload() {
103 Log.e(TAG, "beginDownload" + appContent.getUrl());
104 appContent.setStatus(AppContent.Status.WAITING);
105long blockLength = fileLength / THREAD_NUM;
106for (int i = 0; i < THREAD_NUM; i++) {
107long beginPosition = i * blockLength;//每条线程下载的开始位置108long endPosition = (i + 1) * blockLength;//每条线程下载的结束位置109if (i == (THREAD_NUM - 1)) {
110 endPosition = fileLength;//如果整个文件的大小不为线程个数的整数倍,则最后一个线程的结束位置即为文件的总长度111 }
112 DownloadTask task = new DownloadTask(i, beginPosition, endPosition, this, context);
113 task.executeOnExecutor(THREAD_POOL_EXECUTOR, appContent.getUrl());
114if(tasks == null) {
115 tasks = new ArrayList();
116 }
117 tasks.add(task);
118 }
119 }
120121/**122 * 暂停下载
123*/124publicvoid pause() {
125for (DownloadTask task : tasks) {
126if (task != null && (task.getStatus() == AsyncTask.Status.RUNNING || !task.isCancelled())) {
127 task.cancel(true);
128 }
129 }
130 tasks.clear();
131 appContent.setStatus(AppContent.Status.PAUSED);
132 DownloadFileDAO.getInstance(context.getApplicationContext()).updateDownloadFile(appContent);
133 }
134135/**136 * 将已下载大小归零
137*/138protectedsynchronizedvoid resetDownloadLength() {
139this.downloadLength = 0;
140 }
141142/**143 * 添加已下载大小
144 * 多线程访问需加锁
145 * @param size
146*/147protectedsynchronizedvoid updateDownloadLength(long size){
148this.downloadLength += size;
149//通知更新界面150int percent = (int)((float)downloadLength * 100 / (float)fileLength);
151 appContent.setDownloadPercent(percent);
152if(percent == 100 || downloadLength == fileLength) {
153 appContent.setDownloadPercent(100); //上面计算有时候会有点误差,算到percent=99154 appContent.setStatus(AppContent.Status.FINISHED);
155 DownloadFileDAO.getInstance(context.getApplicationContext()).updateDownloadFile(appContent);
156 }
157 Intent intent = new Intent(Constants.DOWNLOAD_MSG);
158if(appContent.getStatus() == AppContent.Status.WAITING) {
159 appContent.setStatus(AppContent.Status.DOWNLOADING);
160 }
161 Bundle bundle = new Bundle();
162 bundle.putParcelable("appContent", appContent);
163 intent.putExtras(bundle);
164 context.sendBroadcast(intent);
165 }
166167protected String getDownloadPath() {
168return downloadPath;
169 }
170171privateclass InnerHandler extends Handler {
172 @Override
173publicvoid handleMessage(Message msg) {
174switch (msg.what) {
175case GET_LENGTH_SUCCESS :
176 beginDownload();
177break;
178 }
179super.handleMessage(msg);
180 }
181 }
182 }
2、DownloadTask类
1package com.bbk.lling.multitaskdownload.downloador;
2 3import android.content.Context;
4import android.os.AsyncTask;
5import android.util.Log;
6 7import com.bbk.lling.multitaskdownload.beans.DownloadInfo;
8import com.bbk.lling.multitaskdownload.db.DownloadInfoDAO;
9 10import org.apache.http.Header;
11import org.apache.http.HttpResponse;
12import org.apache.http.client.HttpClient;
13import org.apache.http.client.methods.HttpGet;
14import org.apache.http.impl.client.DefaultHttpClient;
15import org.apache.http.message.BasicHeader;
16 17import java.io.File;
18import java.io.IOException;
19import java.io.InputStream;
20import java.io.OutputStream;
21import java.io.RandomAccessFile;
22import java.net.MalformedURLException;
23 24/** 25 * @Class: DownloadTask
26 * @Description: 文件下载AsyncTask
27 * @author: lling(www.cnblogs.com/liuling)
28 * @Date: 2015/10/13
29*/ 30publicclass DownloadTask extends AsyncTask {
31privatestaticfinal String TAG = "DownloadTask";
32 33privateint taskId;
34privatelong beginPosition;
35privatelong endPosition;
36privatelong downloadLength;
37private String url;
38private Downloador downloador;
39private DownloadInfoDAO downloadInfoDAO;
40 41 42public DownloadTask(int taskId, long beginPosition, long endPosition, Downloador downloador,
43 Context context) {
44this.taskId = taskId;
45this.beginPosition = beginPosition;
46this.endPosition = endPosition;
47this.downloador = downloador;
48 downloadInfoDAO = DownloadInfoDAO.getInstance(context.getApplicationContext());
49 }
50 51 @Override
52protectedvoid onPreExecute() {
53 Log.e(TAG, "onPreExecute");
54 }
55 56 @Override
57protectedvoid onPostExecute(Long aLong) {
58 Log.e(TAG, url + "taskId:" + taskId + "executed");
59// downloador.updateDownloadInfo(null); 60 }
61 62 @Override
63protectedvoid onProgressUpdate(Integer... values) {
64//通知downloador增加已下载大小
65// downloador.updateDownloadLength(values[0]); 66 }
67 68 @Override
69protectedvoid onCancelled() {
70 Log.e(TAG, "onCancelled");
71// downloador.updateDownloadInfo(null); 72 }
73 74 @Override
75protected Long doInBackground(String... params) {
76//这里加判断的作用是:如果还处于等待就暂停了,运行到这里已经cancel了,就直接退出 77if(isCancelled()) {
78returnnull;
79 }
80 url = params[0];
81if(url == null) {
82returnnull;
83 }
84 HttpClient client = new DefaultHttpClient();
85 HttpGet request = new HttpGet(url);
86 HttpResponse response;
87 InputStream is;
88 RandomAccessFile fos = null;
89 OutputStream output = null;
90 91 DownloadInfo downloadInfo = null;
92try {
93//本地文件 94 File file = new File(downloador.getDownloadPath() + File.separator + url.substring(url.lastIndexOf("/") + 1));
95 96//获取之前下载保存的信息 97 downloadInfo = downloadInfoDAO.getDownloadInfoByTaskIdAndUrl(taskId, url);
98//从之前结束的位置继续下载
99//这里加了判断file.exists(),判断是否被用户删除了,如果文件没有下载完,但是已经被用户删除了,则重新下载100if(file.exists() && downloadInfo != null) {
101if(downloadInfo.isDownloadSuccess() == 1) {
102//下载完成直接结束103returnnull;
104 }
105 beginPosition = beginPosition + downloadInfo.getDownloadLength();
106 downloadLength = downloadInfo.getDownloadLength();
107 }
108if(!file.exists()) {
109//如果此task已经下载完,但是文件被用户删除,则需要重新设置已下载长度,重新下载110 downloador.resetDownloadLength();
111 }
112113//设置下载的数据位置beginPosition字节到endPosition字节114 Header header_size = new BasicHeader("Range", "bytes=" + beginPosition + "-" + endPosition);
115 request.addHeader(header_size);
116//执行请求获取下载输入流117 response = client.execute(request);
118 is = response.getEntity().getContent();
119120//创建文件输出流121 fos = new RandomAccessFile(file, "rw");
122//从文件的size以后的位置开始写入123 fos.seek(beginPosition);
124125byte buffer [] = newbyte[1024];
126int inputSize = -1;
127while((inputSize = is.read(buffer)) != -1) {
128 fos.write(buffer, 0, inputSize);
129 downloadLength += inputSize;
130 downloador.updateDownloadLength(inputSize);
131132//如果暂停了,需要将下载信息存入数据库133if (isCancelled()) {
134if(downloadInfo == null) {
135 downloadInfo = new DownloadInfo();
136 }
137 downloadInfo.setUrl(url);
138 downloadInfo.setDownloadLength(downloadLength);
139 downloadInfo.setTaskId(taskId);
140 downloadInfo.setDownloadSuccess(0);
141//保存下载信息到数据库142 downloadInfoDAO.insertDownloadInfo(downloadInfo);
143returnnull;
144 }
145 }
146 } catch (MalformedURLException e) {
147 Log.e(TAG, e.getMessage());
148 } catch (IOException e) {
149 Log.e(TAG, e.getMessage());
150 } finally{
151try{
152if (request != null) {
153 request.abort();
154 }
155if(output != null) {
156 output.close();
157 }
158if(fos != null) {
159 fos.close();
160 }
161 } catch(Exception e) {
162 e.printStackTrace();
163 }
164 }
165//执行到这里,说明该task已经下载完了166if(downloadInfo == null) {
167 downloadInfo = new DownloadInfo();
168 }
169 downloadInfo.setUrl(url);
170 downloadInfo.setDownloadLength(downloadLength);
171 downloadInfo.setTaskId(taskId);
172 downloadInfo.setDownloadSuccess(1);
173//保存下载信息到数据库174 downloadInfoDAO.insertDownloadInfo(downloadInfo);
175returnnull;
176 }
177 }