AsyncTask实现多任务多线程断点续传下载

简介:

  这篇博客是AsyncTask下载系列的最后一篇文章,前面写了关于断点续传的和多线程下载的博客,这篇是在前两篇的基础上面实现的,有兴趣的可以去看下。

  一、AsyncTask实现断点续传

  二、AsyncTask实现多线程断点续传

  这里模拟应用市场app下载实现了一个Demo,因为只有一个界面,所以没有将下载放到Service中,而是直接在Activity中创建。在正式的项目中,下载都是放到Service中,然后通过BroadCast通知界面更新进度。


  上代码之前,先看下demo的运行效果图吧。

 

  下面我们看代码,这里每一个文件的下载定义一个Downloador来管理下 载该文件的所有线程(暂停、下载等)。Downloador创建3个DownloadTask(这里定义每个文件分配3个线程下载)来下载该文件特定的起 止位置。这里要通过文件的大小来计算每个线程所下载的起止位置,详细可以参考《AsyncTask实现多线程断点续传》。

  1、Downloador类

  1 package com.bbk.lling.multitaskdownload.downloador;
  2
  3 import android.content.Context;
  4 import android.content.Intent;
  5 import android.os.AsyncTask;
  6 import android.os.Bundle;
  7 import android.os.Handler;
  8 import android.os.Message;
  9 import android.text.TextUtils;
 10 import android.util.Log;
 11 import android.widget.Toast;
 12
 13 import com.bbk.lling.multitaskdownload.beans.AppContent;
 14 import com.bbk.lling.multitaskdownload.beans.DownloadInfo;
 15 import com.bbk.lling.multitaskdownload.db.DownloadFileDAO;
 16 import com.bbk.lling.multitaskdownload.db.DownloadInfoDAO;
 17 import com.bbk.lling.multitaskdownload.utils.DownloadUtils;
 18
 19 import org.apache.http.HttpResponse;
 20 import org.apache.http.client.HttpClient;
 21 import org.apache.http.client.methods.HttpGet;
 22 import org.apache.http.impl.client.DefaultHttpClient;
 23
 24 import java.util.ArrayList;
 25 import java.util.List;
 26 import java.util.concurrent.Executor;
 27 import java.util.concurrent.Executors;
 28
 29 /
 30   @Class: Downloador
 31   @Description: 任务下载器
 32   @author: lling(www.cnblogs.com/liuling)
 33   @Date: 2015/10/13
 34  */
 35 public class Downloador {
 36     public static final String TAG = “Downloador”;
 37     private static final int THREAD_POOL_SIZE = 9;  //线程池大小为9
 38     private static final int THREAD_NUM = 3;  //每个文件3个线程下载
 39     private static final int GET_LENGTH_SUCCESS = 1;
 40     public static final Executor THREAD_POOL_EXECUTOR = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
 41
 42     private List tasks;
 43     private InnerHandler handler = new InnerHandler();
 44
 45     private AppContent appContent; //待下载的应用
 46     private long downloadLength; //下载过程中记录已下载大小
 47     private long fileLength;
 48     private Context context;
 49     private String downloadPath;
 50
 51     public Downloador(Context context, AppContent appContent) {
 52         this.context = context;
 53         this.appContent = appContent;
 54         this.downloadPath = DownloadUtils.getDownloadPath();
 55     }
 56
 57     /
 58       开始下载
 59      /
 60     public void download() {
 61         if(TextUtils.isEmpty(downloadPath)) {
 62             Toast.makeText(context, “未找到SD卡”, Toast.LENGTH_SHORT).show();
 63             return;
 64         }
 65         if(appContent == null) {
 66             throw new IllegalArgumentException(“download content can not be null”);
 67         }
 68         new Thread() {
 69             @Override
 70             public void run() {
 71                 //获取文件大小
 72                 HttpClient client = new DefaultHttpClient();
 73                 HttpGet request = new HttpGet(appContent.getUrl());
 74                 HttpResponse response = null;
 75                 try {
 76                     response = client.execute(request);
 77                     fileLength = response.getEntity().getContentLength();
 78                 } catch (Exception e) {
 79                     Log.e(TAG, e.getMessage());
 80                 } finally {
 81                     if (request != null) {
 82                         request.abort();
 83                     }
 84                 }
 85                 //计算出该文件已经下载的总长度
 86                 List lists = DownloadInfoDAO.getInstance(context.getApplicationContext())
 87                         .getDownloadInfosByUrl(appContent.getUrl());
 88                 for (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      /
102     private void beginDownload() {
103         Log.e(TAG, “beginDownload” + appContent.getUrl());
104         appContent.setStatus(AppContent.Status.WAITING);
105         long blockLength = fileLength / THREAD_NUM;
106         for (int i = 0; i < THREAD_NUM; i++) {
107             long beginPosition = i  blockLength;//每条线程下载的开始位置
108             long endPosition = (i + 1)  blockLength;//每条线程下载的结束位置
109             if (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());
114             if(tasks == null) {
115                 tasks = new ArrayList();
116             }
117             tasks.add(task);
118         }
119     }
120
121     /
122       暂停下载
123      /
124     public void pause() {
125         for (DownloadTask task : tasks) {
126             if (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     }
134
135     /
136       将已下载大小归零
137      /
138     protected synchronized void resetDownloadLength() {
139         this.downloadLength = 0;
140     }
141
142     /
143       添加已下载大小
144       多线程访问需加锁
145       @param size
146      /
147     protected synchronized void updateDownloadLength(long size){
148         this.downloadLength += size;
149         //通知更新界面
150         int percent = (int)((float)downloadLength * 100 / (float)fileLength);
151         appContent.setDownloadPercent(percent);
152         if(percent == 100 || downloadLength == fileLength) {
153             appContent.setDownloadPercent(100); //上面计算有时候会有点误差,算到percent=99
154             appContent.setStatus(AppContent.Status.FINISHED);
155             DownloadFileDAO.getInstance(context.getApplicationContext()).updateDownloadFile(appContent);
156         }
157         Intent intent = new Intent(Constants.DOWNLOAD_MSG);
158         if(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     }
166
167     protected String getDownloadPath() {
168         return downloadPath;
169     }
170
171     private class InnerHandler extends Handler {
172         @Override
173         public void handleMessage(Message msg) {
174             switch (msg.what) {
175                 case GET_LENGTH_SUCCESS :
176                     beginDownload();
177                     break;
178             }
179             super.handleMessage(msg);
180         }
181     }
182 }

  2、DownloadTask类

  1 package com.bbk.lling.multitaskdownload.downloador;
  2
  3 import android.content.Context;
  4 import android.os.AsyncTask;
  5 import android.util.Log;
  6
  7 import com.bbk.lling.multitaskdownload.beans.DownloadInfo;
  8 import com.bbk.lling.multitaskdownload.db.DownloadInfoDAO;
  9
 10 import org.apache.http.Header;
 11 import org.apache.http.HttpResponse;
 12 import org.apache.http.client.HttpClient;
 13 import org.apache.http.client.methods.HttpGet;
 14 import org.apache.http.impl.client.DefaultHttpClient;
 15 import org.apache.http.message.BasicHeader;
 16
 17 import java.io.File;
 18 import java.io.IOException;
 19 import java.io.InputStream;
 20 import java.io.OutputStream;
 21 import java.io.RandomAccessFile;
 22 import java.net.MalformedURLException;
 23
 24 /*
 25   @Class: DownloadTask
 26   @Description: 文件下载AsyncTask
 27   @author: lling(www.cnblogs.com/liuling)
 28   @Date: 2015/10/13
 29  /
 30 public class DownloadTask extends AsyncTask {
 31     private static final String TAG = “DownloadTask”;
 32
 33     private int taskId;
 34     private long beginPosition;
 35     private long endPosition;
 36     private long downloadLength;
 37     private String url;
 38     private Downloador downloador;
 39     private DownloadInfoDAO downloadInfoDAO;
 40
 41
 42     public DownloadTask(int taskId, long beginPosition, long endPosition, Downloador downloador,
 43                         Context context) {
 44         this.taskId = taskId;
 45         this.beginPosition = beginPosition;
 46         this.endPosition = endPosition;
 47         this.downloador = downloador;
 48         downloadInfoDAO = DownloadInfoDAO.getInstance(context.getApplicationContext());
 49     }
 50
 51     @Override
 52     protected void onPreExecute() {
 53         Log.e(TAG, “onPreExecute”);
 54     }
 55
 56     @Override
 57     protected void onPostExecute(Long aLong) {
 58         Log.e(TAG, url + “taskId:” + taskId + “executed”);
 59 //        downloador.updateDownloadInfo(null);
 60     }
 61
 62     @Override
 63     protected void onProgressUpdate(Integer… values) {
 64         //通知downloador增加已下载大小
 65 //        downloador.updateDownloadLength(values[0]);
 66     }
 67
 68     @Override
 69     protected void onCancelled() {
 70         Log.e(TAG, “onCancelled”);
 71 //        downloador.updateDownloadInfo(null);
 72     }
 73
 74     @Override
 75     protected Long doInBackground(String… params) {
 76         //这里加判断的作用是:如果还处于等待就暂停了,运行到这里已经cancel了,就直接退出
 77         if(isCancelled()) {
 78             return null;
 79         }
 80         url = params[0];
 81         if(url == null) {
 82             return null;
 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;
 92         try {
 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(),判断是否被用户删除了,如果文件没有下载完,但是已经被用户删除了,则重新下载
100             if(file.exists() && downloadInfo != null) {
101                 if(downloadInfo.isDownloadSuccess() == 1) {
102                     //下载完成直接结束
103                     return null;
104                 }
105                 beginPosition = beginPosition + downloadInfo.getDownloadLength();
106                 downloadLength = downloadInfo.getDownloadLength();
107             }
108             if(!file.exists()) {
109                 //如果此task已经下载完,但是文件被用户删除,则需要重新设置已下载长度,重新下载
110                 downloador.resetDownloadLength();
111             }
112
113             //设置下载的数据位置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();
119
120             //创建文件输出流
121             fos = new RandomAccessFile(file, “rw”);
122             //从文件的size以后的位置开始写入
123             fos.seek(beginPosition);
124
125             byte buffer [] = new byte[1024];
126             int inputSize = -1;
127             while((inputSize = is.read(buffer)) != -1) {
128                 fos.write(buffer, 0, inputSize);
129                 downloadLength += inputSize;
130                 downloador.updateDownloadLength(inputSize);
131
132                 //如果暂停了,需要将下载信息存入数据库
133                 if (isCancelled()) {
134                     if(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);
143                     return null;
144                 }
145             }
146         } catch (MalformedURLException e) {
147             Log.e(TAG, e.getMessage());
148         } catch (IOException e) {
149             Log.e(TAG, e.getMessage());
150         } finally{
151             try{
152                 if (request != null) {
153                     request.abort();
154                 }
155                 if(output != null) {
156                     output.close();
157                 }
158                 if(fos != null) {
159                     fos.close();
160                 }
161             } catch(Exception e) {
162                 e.printStackTrace();
163             }
164         }
165         //执行到这里,说明该task已经下载完了
166         if(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);
175         return null;
176     }
177 }

  Downloador和DownloadTask只这个例子的核心代码,下面 是关于数据库的,因为要实现断点续传必须要在暂停的时候将每个线程下载的位置记录下来,方便下次继续下载时读取。这里有两个表,一个是存放每个文件的下载 状态的,一个是存放每个文件对应的每个线程的下载状态的。

  3、DBHelper


 1 package com.bbk.lling.multitaskdownload.db;
 2
 3 import android.content.Context;
 4 import android.database.sqlite.SQLiteDatabase;
 5 import android.database.sqlite.SQLiteOpenHelper;
 6
 7 /*
 8   @Class: DBHelper
 9   @Description: 数据库帮助类
10   @author: lling(www.cnblogs.com/liuling)
11   @Date: 2015/10/14
12  /
13 public class DBHelper extends SQLiteOpenHelper {
14
15     public DBHelper(Context context) {
16         super(context, “download.db”, null, 1);
17     }
18
19     @Override
20     public void onCreate(SQLiteDatabase db) {
21         db.execSQL(“create table download_info(_id INTEGER PRIMARY KEY AUTOINCREMENT, task_id INTEGER, “
22                 + “download_length INTEGER, url VARCHAR(255), is_success INTEGER)”);
23         db.execSQL(“create table download_file(_id INTEGER PRIMARY KEY AUTOINCREMENT, app_name VARCHAR(255), “
24                 + “url VARCHAR(255), download_percent INTEGER, status INTEGER)”);
25     }
26
27     @Override
28     public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
29
30     }
31 }

  4、DownloadFileDAO,文件下载状态的数据库操作类

  1 package com.bbk.lling.multitaskdownload.db;
  2
  3 import android.content.Context;
  4 import android.database.Cursor;
  5 import android.database.sqlite.SQLiteDatabase;
  6 import android.text.TextUtils;
  7 import android.util.Log;
  8
  9 import com.bbk.lling.multitaskdownload.beans.AppContent;
 10
 11 import java.util.ArrayList;
 12 import java.util.List;
 13
 14 /
 15   @Class: DownloadFileDAO
 16   @Description: 每个文件下载状态记录的数据库操作类
 17   @author: lling(www.cnblogs.com/liuling)
 18   @Date: 2015/10/13
 19  */
 20 public class DownloadFileDAO {
 21     private static final String TAG = “DownloadFileDAO”;
 22     private static DownloadFileDAO dao=null;
 23     private Context context;
 24     private DownloadFileDAO(Context context) {
 25         this.context=context;
 26     }
 27
 28     synchronized public static DownloadFileDAO getInstance(Context context){
 29         if(dao==null){
 30             dao=new DownloadFileDAO(context);
 31         }
 32         return dao;
 33     }
 34
 35     /
 36       获取数据库连接
 37       @return
 38      /
 39     public SQLiteDatabase getConnection() {
 40         SQLiteDatabase sqliteDatabase = null;
 41         try {
 42             sqliteDatabase= new DBHelper(context).getReadableDatabase();
 43         } catch (Exception e) {
 44             Log.e(TAG, e.getMessage());
 45         }
 46         return sqliteDatabase;
 47     }
 48
 49     /**
 50       插入数据
 51       @param appContent
 52      /
 53     public void insertDownloadFile(AppContent appContent) {
 54         if(appContent == null) {
 55             return;
 56         }
 57         //如果本地已经存在,直接修改
 58         if(getAppContentByUrl(appContent.getUrl()) != null) {
 59             updateDownloadFile(appContent);
 60             return;
 61         }
 62         SQLiteDatabase database = getConnection();
 63         try {
 64             String sql = “insert into download_file(app_name, url, download_percent, status) values (?,?,?,?)”;
 65             Object[] bindArgs = { appContent.getName(), appContent.getUrl(), appContent.getDownloadPercent()
 66                     , appContent.getStatus().getValue()};
 67             database.execSQL(sql, bindArgs);
 68         } catch (Exception e) {
 69             Log.e(TAG, e.getMessage());
 70         } finally {
 71             if (null != database) {
 72                 database.close();
 73             }
 74         }
 75     }
 76
 77     /
 78       根据url获取下载文件信息
 79       @param url
 80       @return
 81      /
 82     public AppContent getAppContentByUrl(String url) {
 83         if(TextUtils.isEmpty(url)) {
 84             return null;
 85         }
 86         SQLiteDatabase database = getConnection();
 87         AppContent appContent = null;
 88         Cursor cursor = null;
 89         try {
 90             String sql = “select * from download_file where url=?”;
 91             cursor = database.rawQuery(sql, new String[] { url });
 92             if (cursor.moveToNext()) {
 93                 appContent = new AppContent(cursor.getString(1), cursor.getString(2));
 94                 appContent.setDownloadPercent(cursor.getInt(3));
 95                 appContent.setStatus(AppContent.Status.getByValue(cursor.getInt(4)));
 96             }
 97         } catch (Exception e) {
 98             Log.e(TAG, e.getMessage());
 99         } finally {
100             if (null != database) {
101                 database.close();
102             }
103             if (null != cursor) {
104                 cursor.close();
105             }
106         }
107         return appContent;
108     }
109
110     /
111       更新下载信息
112       @param appContent
113      /
114     public void updateDownloadFile(AppContent appContent) {
115         if(appContent == null) {
116             return;
117         }
118         SQLiteDatabase database = getConnection();
119         try {
120             Log.e(TAG, “update download_file,app name:” + appContent.getName() + “,url:” + appContent.getUrl()
121                     + “,percent” + appContent.getDownloadPercent() + “,status:” + appContent.getStatus().getValue());
122             String sql = “update download_file set app_name=?, url=?, download_percent=?, status=? where url=?”;
123             Object[] bindArgs = {appContent.getName(), appContent.getUrl(), appContent.getDownloadPercent()
124                     , appContent.getStatus().getValue(), appContent.getUrl()};
125             database.execSQL(sql, bindArgs);
126         } catch (Exception e) {
127             Log.e(TAG, e.getMessage());
128         } finally {
129             if (null != database) {
130                 database.close();
131             }
132         }
133     }
134
135     /**
136       获取所有下载文件记录
137       @return
138      /
139     public List getAll() {
140         SQLiteDatabase database = getConnection();
141         List list = new ArrayList();
142         Cursor cursor = null;
143         try {
144             String sql = “select  from download_file”;
145             cursor = database.rawQuery(sql, null);
146             while (cursor.moveToNext()) {
147                 AppContent appContent = new AppContent(cursor.getString(1), cursor.getString(2));
148                 appContent.setDownloadPercent(cursor.getInt(3));
149                 appContent.setStatus(AppContent.Status.getByValue(cursor.getInt(4)));
150                 list.add(appContent);
151             }
152         } catch (Exception e) {
153             Log.e(TAG, e.getMessage());
154         } finally {
155             if (null != database) {
156                 database.close();
157             }
158             if (null != cursor) {
159                 cursor.close();
160             }
161         }
162         return list;
163     }
164
165     /**
166       根据url删除记录
167       @param url
168      /
169     public void delByUrl(String url) {
170         if(TextUtils.isEmpty(url)) {
171             return;
172         }
173         SQLiteDatabase database = getConnection();
174         try {
175             String sql = “delete from download_file where url=?”;
176             Object[] bindArgs = { url };
177             database.execSQL(sql, bindArgs);
178         } catch (Exception e) {
179             Log.e(TAG, e.getMessage());
180         } finally {
181             if (null != database) {
182                 database.close();
183             }
184         }
185     }
186
187 }

  5、DownloadInfoDAO,每个线程对应下载状态的数据库操作类

  1 package com.bbk.lling.multitaskdownload.db;
  2
  3 import android.content.Context;
  4 import android.database.Cursor;
  5 import android.database.sqlite.SQLiteDatabase;
  6 import android.text.TextUtils;
  7 import android.util.Log;
  8
  9 import com.bbk.lling.multitaskdownload.beans.DownloadInfo;
 10
 11 import java.util.ArrayList;
 12 import java.util.List;
 13
 14 /
 15   @Class: DownloadInfoDAO
 16   @Description: 每个单独线程下载信息记录的数据库操作类
 17   @author: lling(www.cnblogs.com/liuling)
 18   @Date: 2015/10/13
 19  */
 20 public class DownloadInfoDAO {
 21     private static final String TAG = “DownloadInfoDAO”;
 22     private static DownloadInfoDAO dao=null;
 23     private Context context;
 24     private  DownloadInfoDAO(Context context) {
 25         this.context=context;
 26     }
 27
 28     synchronized public static DownloadInfoDAO getInstance(Context context){
 29         if(dao==null){
 30             dao=new DownloadInfoDAO(context);
 31         }
 32         return dao;
 33     }
 34
 35     /
 36       获取数据库连接
 37       @return
 38      /
 39     public SQLiteDatabase getConnection() {
 40         SQLiteDatabase sqliteDatabase = null;
 41         try {
 42             sqliteDatabase= new DBHelper(context).getReadableDatabase();
 43         } catch (Exception e) {
 44             Log.e(TAG, e.getMessage());
 45         }
 46         return sqliteDatabase;
 47     }
 48
 49     /**
 50       插入数据
 51       @param downloadInfo
 52      /
 53     public void insertDownloadInfo(DownloadInfo downloadInfo) {
 54         if(downloadInfo == null) {
 55             return;
 56         }
 57         //如果本地已经存在,直接修改
 58         if(getDownloadInfoByTaskIdAndUrl(downloadInfo.getTaskId(), downloadInfo.getUrl()) != null) {
 59             updateDownloadInfo(downloadInfo);
 60             return;
 61         }
 62         SQLiteDatabase database = getConnection();
 63         try {
 64             String sql = “insert into download_info(task_id, download_length, url, is_success) values (?,?,?,?)”;
 65             Object[] bindArgs = { downloadInfo.getTaskId(), downloadInfo.getDownloadLength(),
 66                     downloadInfo.getUrl(), downloadInfo.isDownloadSuccess()};
 67             database.execSQL(sql, bindArgs);
 68         } catch (Exception e) {
 69             Log.e(TAG, e.getMessage());
 70         } finally {
 71             if (null != database) {
 72                 database.close();
 73             }
 74         }
 75     }
 76
 77     public List getDownloadInfosByUrl(String url) {
 78         if(TextUtils.isEmpty(url)) {
 79             return null;
 80         }
 81         SQLiteDatabase database = getConnection();
 82         List list = new ArrayList();
 83         Cursor cursor = null;
 84         try {
 85             String sql = “select  from download_info where url=?”;
 86             cursor = database.rawQuery(sql, new String[] { url });
 87             while (cursor.moveToNext()) {
 88                 DownloadInfo info = new DownloadInfo();
 89                 info.setTaskId(cursor.getInt(1));
 90                 info.setDownloadLength(cursor.getLong(2));
 91                 info.setDownloadSuccess(cursor.getInt(4));
 92                 info.setUrl(cursor.getString(3));
 93                 list.add(info);
 94             }
 95         } catch (Exception e) {
 96             e.printStackTrace();
 97         } finally {
 98             if (null != database) {
 99                 database.close();
100             }
101             if (null != cursor) {
102                 cursor.close();
103             }
104         }
105         return list;
106     }
107
108     /**
109       根据taskid和url获取下载信息
110       @param taskId
111       @param url
112       @return
113      /
114     public DownloadInfo getDownloadInfoByTaskIdAndUrl(int taskId, String url) {
115         if(TextUtils.isEmpty(url)) {
116             return null;
117         }
118         SQLiteDatabase database = getConnection();
119         DownloadInfo info = null;
120         Cursor cursor = null;
121         try {
122             String sql = “select  from download_info where url=? and task_id=?”;
123             cursor = database.rawQuery(sql, new String[] { url, String.valueOf(taskId) });
124             if (cursor.moveToNext()) {
125                 info = new DownloadInfo();
126                 info.setTaskId(cursor.getInt(1));
127                 info.setDownloadLength(cursor.getLong(2));
128                 info.setDownloadSuccess(cursor.getInt(4));
129                 info.setUrl(cursor.getString(3));
130             }
131         } catch (Exception e) {
132             Log.e(TAG, e.getMessage());
133         } finally {
134             if (null != database) {
135                 database.close();
136             }
137             if (null != cursor) {
138                 cursor.close();
139             }
140         }
141         return info;
142     }
143
144     /**
145       更新下载信息
146       @param downloadInfo
147      /
148     public void updateDownloadInfo(DownloadInfo downloadInfo) {
149         if(downloadInfo == null) {
150             return;
151         }
152         SQLiteDatabase database = getConnection();
153         try {
154             String sql = “update download_info set download_length=?, is_success=? where task_id=? and url=?”;
155             Object[] bindArgs = { downloadInfo.getDownloadLength(), downloadInfo.isDownloadSuccess(),
156                     downloadInfo.getTaskId(), downloadInfo.getUrl() };
157             database.execSQL(sql, bindArgs);
158         } catch (Exception e) {
159             Log.e(TAG, e.getMessage());
160         } finally {
161             if (null != database) {
162                 database.close();
163             }
164         }
165     }
166
167 }

  具体的界面和使用代码我就不贴代码了,代码有点多。需要的可以下载Demo的源码看看。

  因为还没有花太多时间去测,里面难免会有些bug,如果大家发现什么问题,欢迎留言探讨,谢谢!

源码下载:https://github.com/liuling07/MultiTaskAndThreadDownload

 

原创内容,转载请注明出处:http://www.cnblogs.com/liuling/p/2015-10-16-01.html

相关文章
|
3月前
|
数据采集 XML JavaScript
C# 中 ScrapySharp 的多线程下载策略
C# 中 ScrapySharp 的多线程下载策略
|
4月前
|
缓存 Java 调度
Java并发编程:深入解析线程池与Future任务
【7月更文挑战第9天】线程池和Future任务是Java并发编程中非常重要的概念。线程池通过重用线程减少了线程创建和销毁的开销,提高了资源利用率。而Future接口则提供了检查异步任务状态和获取任务结果的能力,使得异步编程更加灵活和强大。掌握这些概念,将有助于我们编写出更高效、更可靠的并发程序。
|
1月前
|
缓存 负载均衡 Java
c++写高性能的任务流线程池(万字详解!)
本文介绍了一种高性能的任务流线程池设计,涵盖多种优化机制。首先介绍了Work Steal机制,通过任务偷窃提高资源利用率。接着讨论了优先级任务,使不同优先级的任务得到合理调度。然后提出了缓存机制,通过环形缓存队列提升程序负载能力。Local Thread机制则通过预先创建线程减少创建和销毁线程的开销。Lock Free机制进一步减少了锁的竞争。容量动态调整机制根据任务负载动态调整线程数量。批量处理机制提高了任务处理效率。此外,还介绍了负载均衡、避免等待、预测优化、减少复制等策略。最后,任务组的设计便于管理和复用多任务。整体设计旨在提升线程池的性能和稳定性。
72 5
|
3月前
|
前端开发 JavaScript 大数据
React与Web Workers:开启前端多线程时代的钥匙——深入探索计算密集型任务的优化策略与最佳实践
【8月更文挑战第31天】随着Web应用复杂性的提升,单线程JavaScript已难以胜任高计算量任务。Web Workers通过多线程编程解决了这一问题,使耗时任务独立运行而不阻塞主线程。结合React的组件化与虚拟DOM优势,可将大数据处理等任务交由Web Workers完成,确保UI流畅。最佳实践包括定义清晰接口、加强错误处理及合理评估任务特性。这一结合不仅提升了用户体验,更为前端开发带来多线程时代的全新可能。
67 1
|
3月前
|
存储 监控 Java
|
3月前
|
数据处理 Python
解锁Python多线程编程魔法,告别漫长等待!让数据下载如飞,感受科技带来的速度与激情!
【8月更文挑战第22天】Python以简洁的语法和强大的库支持在多个领域大放异彩。尽管存在全局解释器锁(GIL),Python仍提供多线程支持,尤其适用于I/O密集型任务。通过一个多线程下载数据的例子,展示了如何使用`threading`模块创建多线程程序,并与单线程版本进行了性能对比。实验表明,多线程能显著减少总等待时间,但在CPU密集型任务上GIL可能会限制其性能提升。此案例帮助理解Python多线程的优势及其适用场景。
38 0
|
4月前
|
Java Linux
Java演进问题之1:1线程模型对于I/O密集型任务如何解决
Java演进问题之1:1线程模型对于I/O密集型任务如何解决
|
3月前
|
Cloud Native Java 调度
项目环境测试问题之线程同步器会造成执行完任务的worker等待的情况如何解决
项目环境测试问题之线程同步器会造成执行完任务的worker等待的情况如何解决
|
3月前
|
Java 测试技术 PHP
父子任务使用不当线程池死锁怎么解决?
在Java多线程编程中,线程池有助于提升性能与资源利用效率,但若父子任务共用同一池,则可能诱发死锁。本文通过一个具体案例剖析此问题:在一个固定大小为2的线程池中,父任务直接调用`outerTask`,而`outerTask`再次使用同一线程池异步调用`innerTask`。理论上,任务应迅速完成,但实际上却超时未完成。经由`jstack`输出的线程调用栈分析发现,线程陷入等待状态,形成“死锁”。原因是子任务需待父任务完成,而父任务则需等待子任务执行完毕以释放线程,从而相互阻塞。此问题在测试环境中不易显现,常在生产环境下高并发时爆发,重启或扩容仅能暂时缓解。
|
4月前
|
设计模式 安全 Java
Java面试题:设计模式如单例模式、工厂模式、观察者模式等在多线程环境下线程安全问题,Java内存模型定义了线程如何与内存交互,包括原子性、可见性、有序性,并发框架提供了更高层次的并发任务处理能力
Java面试题:设计模式如单例模式、工厂模式、观察者模式等在多线程环境下线程安全问题,Java内存模型定义了线程如何与内存交互,包括原子性、可见性、有序性,并发框架提供了更高层次的并发任务处理能力
74 1