国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Android > 正文

Android 使用AsyncTask實現多任務多線程斷點續傳下載

2019-10-22 18:10:23
字體:
來源:轉載
供稿:網友

這篇博客是AsyncTask下載系列的最后一篇文章,前面寫了關于斷點續傳的和多線程下載的博客,這篇是在前兩篇的基礎上面實現的,有興趣的可以去看下。

  一、AsyncTask實現斷點續傳

  二、AsyncTask實現多線程斷點續傳

  這里模擬應用市場app下載實現了一個Demo,因為只有一個界面,所以沒有將下載放到Service中,而是直接在Activity中創建。在正式的項目中,下載都是放到Service中,然后通過BroadCast通知界面更新進度。

  上代碼之前,先看下demo的運行效果圖吧。

Android,AsyncTask,多任務,多線程,斷點續傳

  下面我們看代碼,這里每一個文件的下載定義一個Downloador來管理下載該文件的所有線程(暫停、下載等)。Downloador創建3個DownloadTask(這里定義每個文件分配3個線程下載)來下載該文件特定的起止位置。這里要通過文件的大小來計算每個線程所下載的起止位置,詳細可以參考《AsyncTask實現多線程斷點續傳》。

  1、Downloador類

package com.bbk.lling.multitaskdownload.downloador;import android.content.Context;import android.content.Intent;import android.os.AsyncTask;import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.text.TextUtils;import android.util.Log;import android.widget.Toast;import com.bbk.lling.multitaskdownload.beans.AppContent;import com.bbk.lling.multitaskdownload.beans.DownloadInfo;import com.bbk.lling.multitaskdownload.db.DownloadFileDAO;import com.bbk.lling.multitaskdownload.db.DownloadInfoDAO;import com.bbk.lling.multitaskdownload.utils.DownloadUtils;import org.apache.http.HttpResponse;import org.apache.http.client.HttpClient;import org.apache.http.client.methods.HttpGet;import org.apache.http.impl.client.DefaultHttpClient;import java.util.ArrayList;import java.util.List;import java.util.concurrent.Executor;import java.util.concurrent.Executors;/** * @Class: Downloador * @Description: 任務下載器 * @author: lling(www.cnblogs.com/liuling) * @Date: 2015/10/13 */public class Downloador { public static final String TAG = "Downloador"; private static final int THREAD_POOL_SIZE = 9; //線程池大小為9 private static final int THREAD_NUM = 3; //每個文件3個線程下載 private static final int GET_LENGTH_SUCCESS = 1; public static final Executor THREAD_POOL_EXECUTOR = Executors.newFixedThreadPool(THREAD_POOL_SIZE); private List<DownloadTask> tasks; private InnerHandler handler = new InnerHandler(); private AppContent appContent; //待下載的應用 private long downloadLength; //下載過程中記錄已下載大小 private long fileLength; private Context context; private String downloadPath; public Downloador(Context context, AppContent appContent) { this.context = context; this.appContent = appContent; this.downloadPath = DownloadUtils.getDownloadPath(); } /** * 開始下載 */ public void download() { if(TextUtils.isEmpty(downloadPath)) { Toast.makeText(context, "未找到SD卡", Toast.LENGTH_SHORT).show(); return; } if(appContent == null) { throw new IllegalArgumentException("download content can not be null"); } new Thread() { @Override public void run() { //獲取文件大小 HttpClient client = new DefaultHttpClient(); HttpGet request = new HttpGet(appContent.getUrl()); HttpResponse response = null; try {  response = client.execute(request);  fileLength = response.getEntity().getContentLength(); } catch (Exception e) {  Log.e(TAG, e.getMessage()); } finally {  if (request != null) {  request.abort();  } } //計算出該文件已經下載的總長度 List<DownloadInfo> lists = DownloadInfoDAO.getInstance(context.getApplicationContext())  .getDownloadInfosByUrl(appContent.getUrl()); for (DownloadInfo info : lists) {  downloadLength += info.getDownloadLength(); } //插入文件下載記錄到數據庫 DownloadFileDAO.getInstance(context.getApplicationContext()).insertDownloadFile(appContent); Message.obtain(handler, GET_LENGTH_SUCCESS).sendToTarget(); } }.start(); } /** * 開始創建AsyncTask下載 */ private void beginDownload() { Log.e(TAG, "beginDownload" + appContent.getUrl()); appContent.setStatus(AppContent.Status.WAITING); long blockLength = fileLength / THREAD_NUM; for (int i = 0; i < THREAD_NUM; i++) { long beginPosition = i * blockLength;//每條線程下載的開始位置 long endPosition = (i + 1) * blockLength;//每條線程下載的結束位置 if (i == (THREAD_NUM - 1)) { endPosition = fileLength;//如果整個文件的大小不為線程個數的整數倍,則最后一個線程的結束位置即為文件的總長度 } DownloadTask task = new DownloadTask(i, beginPosition, endPosition, this, context); task.executeOnExecutor(THREAD_POOL_EXECUTOR, appContent.getUrl()); if(tasks == null) { tasks = new ArrayList<DownloadTask>(); } tasks.add(task); } } /** * 暫停下載 */ public void pause() { for (DownloadTask task : tasks) { if (task != null && (task.getStatus() == AsyncTask.Status.RUNNING || !task.isCancelled())) { task.cancel(true); } } tasks.clear(); appContent.setStatus(AppContent.Status.PAUSED); DownloadFileDAO.getInstance(context.getApplicationContext()).updateDownloadFile(appContent); } /** * 將已下載大小歸零 */ protected synchronized void resetDownloadLength() { this.downloadLength = 0; } /** * 添加已下載大小 * 多線程訪問需加鎖 * @param size */ protected synchronized void updateDownloadLength(long size){ this.downloadLength += size; //通知更新界面 int percent = (int)((float)downloadLength * 100 / (float)fileLength); appContent.setDownloadPercent(percent); if(percent == 100 || downloadLength == fileLength) { appContent.setDownloadPercent(100); //上面計算有時候會有點誤差,算到percent=99 appContent.setStatus(AppContent.Status.FINISHED); DownloadFileDAO.getInstance(context.getApplicationContext()).updateDownloadFile(appContent); } Intent intent = new Intent(Constants.DOWNLOAD_MSG); if(appContent.getStatus() == AppContent.Status.WAITING) { appContent.setStatus(AppContent.Status.DOWNLOADING); } Bundle bundle = new Bundle(); bundle.putParcelable("appContent", appContent); intent.putExtras(bundle); context.sendBroadcast(intent); } protected String getDownloadPath() { return downloadPath; } private class InnerHandler extends Handler { @Override public void handleMessage(Message msg) { switch (msg.what) { case GET_LENGTH_SUCCESS :  beginDownload();  break; } super.handleMessage(msg); } }}

 2、DownloadTask類

package com.bbk.lling.multitaskdownload.downloador;import android.content.Context;import android.os.AsyncTask;import android.util.Log;import com.bbk.lling.multitaskdownload.beans.DownloadInfo;import com.bbk.lling.multitaskdownload.db.DownloadInfoDAO;import org.apache.http.Header;import org.apache.http.HttpResponse;import org.apache.http.client.HttpClient;import org.apache.http.client.methods.HttpGet;import org.apache.http.impl.client.DefaultHttpClient;import org.apache.http.message.BasicHeader;import java.io.File;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.io.RandomAccessFile;import java.net.MalformedURLException;/** * @Class: DownloadTask * @Description: 文件下載AsyncTask * @author: lling(www.cnblogs.com/liuling) * @Date: 2015/10/13 */public class DownloadTask extends AsyncTask<String, Integer , Long> { private static final String TAG = "DownloadTask"; private int taskId; private long beginPosition; private long endPosition; private long downloadLength; private String url; private Downloador downloador; private DownloadInfoDAO downloadInfoDAO; public DownloadTask(int taskId, long beginPosition, long endPosition, Downloador downloador,  Context context) { this.taskId = taskId; this.beginPosition = beginPosition; this.endPosition = endPosition; this.downloador = downloador; downloadInfoDAO = DownloadInfoDAO.getInstance(context.getApplicationContext()); } @Override protected void onPreExecute() { Log.e(TAG, "onPreExecute"); } @Override protected void onPostExecute(Long aLong) { Log.e(TAG, url + "taskId:" + taskId + "executed");// downloador.updateDownloadInfo(null); } @Override protected void onProgressUpdate(Integer... values) { //通知downloador增加已下載大小// downloador.updateDownloadLength(values[0]); } @Override protected void onCancelled() { Log.e(TAG, "onCancelled");// downloador.updateDownloadInfo(null); } @Override protected Long doInBackground(String... params) { //這里加判斷的作用是:如果還處于等待就暫停了,運行到這里已經cancel了,就直接退出 if(isCancelled()) { return null; } url = params[0]; if(url == null) { return null; } HttpClient client = new DefaultHttpClient(); HttpGet request = new HttpGet(url); HttpResponse response; InputStream is; RandomAccessFile fos = null; OutputStream output = null; DownloadInfo downloadInfo = null; try { //本地文件 File file = new File(downloador.getDownloadPath() + File.separator + url.substring(url.lastIndexOf("/") + 1)); //獲取之前下載保存的信息 downloadInfo = downloadInfoDAO.getDownloadInfoByTaskIdAndUrl(taskId, url); //從之前結束的位置繼續下載 //這里加了判斷file.exists(),判斷是否被用戶刪除了,如果文件沒有下載完,但是已經被用戶刪除了,則重新下載 if(file.exists() && downloadInfo != null) { if(downloadInfo.isDownloadSuccess() == 1) {  //下載完成直接結束  return null; } beginPosition = beginPosition + downloadInfo.getDownloadLength(); downloadLength = downloadInfo.getDownloadLength(); } if(!file.exists()) { //如果此task已經下載完,但是文件被用戶刪除,則需要重新設置已下載長度,重新下載 downloador.resetDownloadLength(); } //設置下載的數據位置beginPosition字節到endPosition字節 Header header_size = new BasicHeader("Range", "bytes=" + beginPosition + "-" + endPosition); request.addHeader(header_size); //執行請求獲取下載輸入流 response = client.execute(request); is = response.getEntity().getContent(); //創建文件輸出流 fos = new RandomAccessFile(file, "rw"); //從文件的size以后的位置開始寫入 fos.seek(beginPosition); byte buffer [] = new byte[1024]; int inputSize = -1; while((inputSize = is.read(buffer)) != -1) { fos.write(buffer, 0, inputSize); downloadLength += inputSize; downloador.updateDownloadLength(inputSize); //如果暫停了,需要將下載信息存入數據庫 if (isCancelled()) {  if(downloadInfo == null) {  downloadInfo = new DownloadInfo();  }  downloadInfo.setUrl(url);  downloadInfo.setDownloadLength(downloadLength);  downloadInfo.setTaskId(taskId);  downloadInfo.setDownloadSuccess(0);  //保存下載信息到數據庫  downloadInfoDAO.insertDownloadInfo(downloadInfo);  return null; } } } catch (MalformedURLException e) { Log.e(TAG, e.getMessage()); } catch (IOException e) { Log.e(TAG, e.getMessage()); } finally{ try{ if (request != null) {  request.abort(); } if(output != null) {  output.close(); } if(fos != null) {  fos.close(); } } catch(Exception e) { e.printStackTrace(); } } //執行到這里,說明該task已經下載完了 if(downloadInfo == null) { downloadInfo = new DownloadInfo(); } downloadInfo.setUrl(url); downloadInfo.setDownloadLength(downloadLength); downloadInfo.setTaskId(taskId); downloadInfo.setDownloadSuccess(1); //保存下載信息到數據庫 downloadInfoDAO.insertDownloadInfo(downloadInfo); return null; }}

Downloador和DownloadTask只這個例子的核心代碼,下面是關于數據庫的,因為要實現斷點續傳必須要在暫停的時候將每個線程下載的位置記錄下來,方便下次繼續下載時讀取。這里有兩個表,一個是存放每個文件的下載狀態的,一個是存放每個文件對應的每個線程的下載狀態的。

  3、DBHelper

package com.bbk.lling.multitaskdownload.db;import android.content.Context;import android.database.sqlite.SQLiteDatabase;import android.database.sqlite.SQLiteOpenHelper;/** * @Class: DBHelper * @Description: 數據庫幫助類 * @author: lling(www.cnblogs.com/liuling) * @Date: 2015/10/14 */public class DBHelper extends SQLiteOpenHelper { public DBHelper(Context context) { super(context, "download.db", null, 1); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL("create table download_info(_id INTEGER PRIMARY KEY AUTOINCREMENT, task_id INTEGER, " + "download_length INTEGER, url VARCHAR(255), is_success INTEGER)"); db.execSQL("create table download_file(_id INTEGER PRIMARY KEY AUTOINCREMENT, app_name VARCHAR(255), " + "url VARCHAR(255), download_percent INTEGER, status INTEGER)"); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { }}

4、DownloadFileDAO,文件下載狀態的數據庫操作類

package com.bbk.lling.multitaskdownload.db;import android.content.Context;import android.database.Cursor;import android.database.sqlite.SQLiteDatabase;import android.text.TextUtils;import android.util.Log;import com.bbk.lling.multitaskdownload.beans.AppContent;import java.util.ArrayList;import java.util.List;/** * @Class: DownloadFileDAO * @Description: 每個文件下載狀態記錄的數據庫操作類 * @author: lling(www.cnblogs.com/liuling) * @Date: 2015/10/13 */public class DownloadFileDAO { private static final String TAG = "DownloadFileDAO"; private static DownloadFileDAO dao=null; private Context context; private DownloadFileDAO(Context context) { this.context=context; } synchronized public static DownloadFileDAO getInstance(Context context){ if(dao==null){ dao=new DownloadFileDAO(context); } return dao; } /** * 獲取數據庫連接 * @return */ public SQLiteDatabase getConnection() { SQLiteDatabase sqliteDatabase = null; try { sqliteDatabase= new DBHelper(context).getReadableDatabase(); } catch (Exception e) { Log.e(TAG, e.getMessage()); } return sqliteDatabase; } /** * 插入數據 * @param appContent */ public void insertDownloadFile(AppContent appContent) { if(appContent == null) { return; } //如果本地已經存在,直接修改 if(getAppContentByUrl(appContent.getUrl()) != null) { updateDownloadFile(appContent); return; } SQLiteDatabase database = getConnection(); try { String sql = "insert into download_file(app_name, url, download_percent, status) values (?,?,?,?)"; Object[] bindArgs = { appContent.getName(), appContent.getUrl(), appContent.getDownloadPercent()  , appContent.getStatus().getValue()}; database.execSQL(sql, bindArgs); } catch (Exception e) { Log.e(TAG, e.getMessage()); } finally { if (null != database) { database.close(); } } } /** * 根據url獲取下載文件信息 * @param url * @return */ public AppContent getAppContentByUrl(String url) { if(TextUtils.isEmpty(url)) { return null; } SQLiteDatabase database = getConnection(); AppContent appContent = null; Cursor cursor = null; try { String sql = "select * from download_file where url=?"; cursor = database.rawQuery(sql, new String[] { url }); if (cursor.moveToNext()) { appContent = new AppContent(cursor.getString(1), cursor.getString(2)); appContent.setDownloadPercent(cursor.getInt(3)); appContent.setStatus(AppContent.Status.getByValue(cursor.getInt(4))); } } catch (Exception e) { Log.e(TAG, e.getMessage()); } finally { if (null != database) { database.close(); } if (null != cursor) { cursor.close(); } } return appContent; } /** * 更新下載信息 * @param appContent */ public void updateDownloadFile(AppContent appContent) { if(appContent == null) { return; } SQLiteDatabase database = getConnection(); try { Log.e(TAG, "update download_file,app name:" + appContent.getName() + ",url:" + appContent.getUrl()  + ",percent" + appContent.getDownloadPercent() + ",status:" + appContent.getStatus().getValue()); String sql = "update download_file set app_name=?, url=?, download_percent=?, status=? where url=?"; Object[] bindArgs = {appContent.getName(), appContent.getUrl(), appContent.getDownloadPercent()  , appContent.getStatus().getValue(), appContent.getUrl()}; database.execSQL(sql, bindArgs); } catch (Exception e) { Log.e(TAG, e.getMessage()); } finally { if (null != database) { database.close(); } } } /** * 獲取所有下載文件記錄 * @return */ public List<AppContent> getAll() { SQLiteDatabase database = getConnection(); List<AppContent> list = new ArrayList<AppContent>(); Cursor cursor = null; try { String sql = "select * from download_file"; cursor = database.rawQuery(sql, null); while (cursor.moveToNext()) { AppContent appContent = new AppContent(cursor.getString(1), cursor.getString(2)); appContent.setDownloadPercent(cursor.getInt(3)); appContent.setStatus(AppContent.Status.getByValue(cursor.getInt(4))); list.add(appContent); } } catch (Exception e) { Log.e(TAG, e.getMessage()); } finally { if (null != database) { database.close(); } if (null != cursor) { cursor.close(); } } return list; } /** * 根據url刪除記錄 * @param url */ public void delByUrl(String url) { if(TextUtils.isEmpty(url)) { return; } SQLiteDatabase database = getConnection(); try { String sql = "delete from download_file where url=?"; Object[] bindArgs = { url }; database.execSQL(sql, bindArgs); } catch (Exception e) { Log.e(TAG, e.getMessage()); } finally { if (null != database) { database.close(); } } }}

5、DownloadInfoDAO,每個線程對應下載狀態的數據庫操作類

package com.bbk.lling.multitaskdownload.db;import android.content.Context;import android.database.Cursor;import android.database.sqlite.SQLiteDatabase;import android.text.TextUtils;import android.util.Log;import com.bbk.lling.multitaskdownload.beans.DownloadInfo;import java.util.ArrayList;import java.util.List;/** * @Class: DownloadInfoDAO * @Description: 每個單獨線程下載信息記錄的數據庫操作類 * @author: lling(www.cnblogs.com/liuling) * @Date: 2015/10/13 */public class DownloadInfoDAO { private static final String TAG = "DownloadInfoDAO"; private static DownloadInfoDAO dao=null; private Context context; private DownloadInfoDAO(Context context) { this.context=context; } synchronized public static DownloadInfoDAO getInstance(Context context){ if(dao==null){ dao=new DownloadInfoDAO(context); } return dao; } /** * 獲取數據庫連接 * @return */ public SQLiteDatabase getConnection() { SQLiteDatabase sqliteDatabase = null; try { sqliteDatabase= new DBHelper(context).getReadableDatabase(); } catch (Exception e) { Log.e(TAG, e.getMessage()); } return sqliteDatabase; } /** * 插入數據 * @param downloadInfo */ public void insertDownloadInfo(DownloadInfo downloadInfo) { if(downloadInfo == null) { return; } //如果本地已經存在,直接修改 if(getDownloadInfoByTaskIdAndUrl(downloadInfo.getTaskId(), downloadInfo.getUrl()) != null) { updateDownloadInfo(downloadInfo); return; } SQLiteDatabase database = getConnection(); try { String sql = "insert into download_info(task_id, download_length, url, is_success) values (?,?,?,?)"; Object[] bindArgs = { downloadInfo.getTaskId(), downloadInfo.getDownloadLength(),  downloadInfo.getUrl(), downloadInfo.isDownloadSuccess()}; database.execSQL(sql, bindArgs); } catch (Exception e) { Log.e(TAG, e.getMessage()); } finally { if (null != database) { database.close(); } } } public List<DownloadInfo> getDownloadInfosByUrl(String url) { if(TextUtils.isEmpty(url)) { return null; } SQLiteDatabase database = getConnection(); List<DownloadInfo> list = new ArrayList<DownloadInfo>(); Cursor cursor = null; try { String sql = "select * from download_info where url=?"; cursor = database.rawQuery(sql, new String[] { url }); while (cursor.moveToNext()) { DownloadInfo info = new DownloadInfo(); info.setTaskId(cursor.getInt(1)); info.setDownloadLength(cursor.getLong(2)); info.setDownloadSuccess(cursor.getInt(4)); info.setUrl(cursor.getString(3)); list.add(info); } } catch (Exception e) { e.printStackTrace(); } finally { if (null != database) { database.close(); } if (null != cursor) { cursor.close(); } } return list; } /** * 根據taskid和url獲取下載信息 * @param taskId * @param url * @return */ public DownloadInfo getDownloadInfoByTaskIdAndUrl(int taskId, String url) { if(TextUtils.isEmpty(url)) { return null; } SQLiteDatabase database = getConnection(); DownloadInfo info = null; Cursor cursor = null; try { String sql = "select * from download_info where url=? and task_id=?"; cursor = database.rawQuery(sql, new String[] { url, String.valueOf(taskId) }); if (cursor.moveToNext()) { info = new DownloadInfo(); info.setTaskId(cursor.getInt(1)); info.setDownloadLength(cursor.getLong(2)); info.setDownloadSuccess(cursor.getInt(4)); info.setUrl(cursor.getString(3)); } } catch (Exception e) { Log.e(TAG, e.getMessage()); } finally { if (null != database) { database.close(); } if (null != cursor) { cursor.close(); } } return info; } /** * 更新下載信息 * @param downloadInfo */ public void updateDownloadInfo(DownloadInfo downloadInfo) { if(downloadInfo == null) { return; } SQLiteDatabase database = getConnection(); try { String sql = "update download_info set download_length=?, is_success=? where task_id=? and url=?"; Object[] bindArgs = { downloadInfo.getDownloadLength(), downloadInfo.isDownloadSuccess(),  downloadInfo.getTaskId(), downloadInfo.getUrl() }; database.execSQL(sql, bindArgs); } catch (Exception e) { Log.e(TAG, e.getMessage()); } finally { if (null != database) { database.close(); } } }}

具體的界面和使用代碼我就不貼代碼了,代碼有點多。需要的可以下載Demo的源碼看看。

  因為還沒有花太多時間去測,里面難免會有些bug,如果大家發現什么問題,歡迎留言探討,謝謝!  

源碼下載:https://github.com/liuling07/MultiTaskAndThreadDownload


注:相關教程知識閱讀請移步到Android開發頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 漾濞| 兴宁市| 洪江市| 天祝| 江山市| 辽宁省| 墨竹工卡县| 德钦县| 昌吉市| 泰和县| 德昌县| 工布江达县| 漯河市| 且末县| 长寿区| 汕头市| 高密市| 常州市| 巧家县| 永泰县| 临西县| 涪陵区| 哈巴河县| 广安市| 临朐县| 迁西县| 麻栗坡县| 珲春市| 汕头市| 井冈山市| 社会| 祁东县| 保康县| 乌拉特中旗| 财经| 班戈县| 公主岭市| 沈丘县| 西宁市| 平潭县| 嘉定区|