您好,登錄后才能下訂單哦!
怎么在Android中使用AsyncTask實(shí)現(xiàn)一個(gè)多任務(wù)多線程斷點(diǎn)續(xù)傳下載功能?相信很多沒(méi)有經(jīng)驗(yàn)的人對(duì)此束手無(wú)策,為此本文總結(jié)了問(wèn)題出現(xiàn)的原因和解決方法,通過(guò)這篇文章希望你能解決這個(gè)問(wèn)題。
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: 任務(wù)下載器 * @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; //每個(gè)文件3個(gè)線程下載 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; //待下載的應(yīng)用 private long downloadLength; //下載過(guò)程中記錄已下載大小 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(); } /** * 開(kāi)始下載 */ 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(); } } //計(jì)算出該文件已經(jīng)下載的總長(zhǎng)度 List<DownloadInfo> lists = DownloadInfoDAO.getInstance(context.getApplicationContext()) .getDownloadInfosByUrl(appContent.getUrl()); for (DownloadInfo info : lists) { downloadLength += info.getDownloadLength(); } //插入文件下載記錄到數(shù)據(jù)庫(kù) DownloadFileDAO.getInstance(context.getApplicationContext()).insertDownloadFile(appContent); Message.obtain(handler, GET_LENGTH_SUCCESS).sendToTarget(); } }.start(); } /** * 開(kāi)始創(chuàng)建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;//每條線程下載的開(kāi)始位置 long endPosition = (i + 1) * blockLength;//每條線程下載的結(jié)束位置 if (i == (THREAD_NUM - 1)) { endPosition = fileLength;//如果整個(gè)文件的大小不為線程個(gè)數(shù)的整數(shù)倍,則最后一個(gè)線程的結(jié)束位置即為文件的總長(zhǎng)度 } 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; } /** * 添加已下載大小 * 多線程訪問(wèn)需加鎖 * @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); //上面計(jì)算有時(shí)候會(huì)有點(diǎn)誤差,算到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) { //這里加判斷的作用是:如果還處于等待就暫停了,運(yùn)行到這里已經(jīng)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); //從之前結(jié)束的位置繼續(xù)下載 //這里加了判斷file.exists(),判斷是否被用戶刪除了,如果文件沒(méi)有下載完,但是已經(jīng)被用戶刪除了,則重新下載 if(file.exists() && downloadInfo != null) { if(downloadInfo.isDownloadSuccess() == 1) { //下載完成直接結(jié)束 return null; } beginPosition = beginPosition + downloadInfo.getDownloadLength(); downloadLength = downloadInfo.getDownloadLength(); } if(!file.exists()) { //如果此task已經(jīng)下載完,但是文件被用戶刪除,則需要重新設(shè)置已下載長(zhǎng)度,重新下載 downloador.resetDownloadLength(); } //設(shè)置下載的數(shù)據(jù)位置beginPosition字節(jié)到endPosition字節(jié) Header header_size = new BasicHeader("Range", "bytes=" + beginPosition + "-" + endPosition); request.addHeader(header_size); //執(zhí)行請(qǐng)求獲取下載輸入流 response = client.execute(request); is = response.getEntity().getContent(); //創(chuàng)建文件輸出流 fos = new RandomAccessFile(file, "rw"); //從文件的size以后的位置開(kāi)始寫(xiě)入 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); //如果暫停了,需要將下載信息存入數(shù)據(jù)庫(kù) if (isCancelled()) { if(downloadInfo == null) { downloadInfo = new DownloadInfo(); } downloadInfo.setUrl(url); downloadInfo.setDownloadLength(downloadLength); downloadInfo.setTaskId(taskId); downloadInfo.setDownloadSuccess(0); //保存下載信息到數(shù)據(jù)庫(kù) 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(); } } //執(zhí)行到這里,說(shuō)明該task已經(jīng)下載完了 if(downloadInfo == null) { downloadInfo = new DownloadInfo(); } downloadInfo.setUrl(url); downloadInfo.setDownloadLength(downloadLength); downloadInfo.setTaskId(taskId); downloadInfo.setDownloadSuccess(1); //保存下載信息到數(shù)據(jù)庫(kù) downloadInfoDAO.insertDownloadInfo(downloadInfo); return null; } }
Downloador和DownloadTask只這個(gè)例子的核心代碼,下面是關(guān)于數(shù)據(jù)庫(kù)的,因?yàn)橐獙?shí)現(xiàn)斷點(diǎn)續(xù)傳必須要在暫停的時(shí)候?qū)⒚總€(gè)線程下載的位置記錄下來(lái),方便下次繼續(xù)下載時(shí)讀取。這里有兩個(gè)表,一個(gè)是存放每個(gè)文件的下載狀態(tài)的,一個(gè)是存放每個(gè)文件對(duì)應(yīng)的每個(gè)線程的下載狀態(tài)的。
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: 數(shù)據(jù)庫(kù)幫助類 * @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,文件下載狀態(tài)的數(shù)據(jù)庫(kù)操作類
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: 每個(gè)文件下載狀態(tài)記錄的數(shù)據(jù)庫(kù)操作類 * @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; } /** * 獲取數(shù)據(jù)庫(kù)連接 * @return */ public SQLiteDatabase getConnection() { SQLiteDatabase sqliteDatabase = null; try { sqliteDatabase= new DBHelper(context).getReadableDatabase(); } catch (Exception e) { Log.e(TAG, e.getMessage()); } return sqliteDatabase; } /** * 插入數(shù)據(jù) * @param appContent */ public void insertDownloadFile(AppContent appContent) { if(appContent == null) { return; } //如果本地已經(jīng)存在,直接修改 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(); } } } /** * 根據(jù)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; } /** * 根據(jù)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,每個(gè)線程對(duì)應(yīng)下載狀態(tài)的數(shù)據(jù)庫(kù)操作類
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: 每個(gè)單獨(dú)線程下載信息記錄的數(shù)據(jù)庫(kù)操作類 * @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; } /** * 獲取數(shù)據(jù)庫(kù)連接 * @return */ public SQLiteDatabase getConnection() { SQLiteDatabase sqliteDatabase = null; try { sqliteDatabase= new DBHelper(context).getReadableDatabase(); } catch (Exception e) { Log.e(TAG, e.getMessage()); } return sqliteDatabase; } /** * 插入數(shù)據(jù) * @param downloadInfo */ public void insertDownloadInfo(DownloadInfo downloadInfo) { if(downloadInfo == null) { return; } //如果本地已經(jīng)存在,直接修改 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; } /** * 根據(jù)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(); } } } }
看完上述內(nèi)容,你們掌握怎么在Android中使用AsyncTask實(shí)現(xiàn)一個(gè)多任務(wù)多線程斷點(diǎn)續(xù)傳下載功能的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注億速云行業(yè)資訊頻道,感謝各位的閱讀!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。