【Android】多线程下载加断点续传
http://blog.csdn.net/smbroe/article/details/42270573
?????? 文件下載在App應(yīng)用中也用到很多,一般版本更新時多要用的文件下載來進行處理,以前也有看過很多大神有過該方面的博客,今天我也自己來實踐一下,寫的一般,還請大家多提意見,共同進步。主要思路:
??????? 1.多線程下載:
?????????????? 首先通過下載總線程數(shù)來劃分文件的下載區(qū)域:利用int range = fileSize / threadCount;得到每一段下載量;每一段的位置是i * range到(i + 1) * rang? - 1,注意最后一段的位置是到filesize - 1;
?????????????? 通過Http協(xié)議的Range字段實現(xiàn)下載文件的分段;
?????????????? 通過Java類RandomAccessFile可以實現(xiàn)文件的隨機訪問,利用seek方法定位的文件的指定位置;
?????????????? 由HttpUrlConnection獲取流來進行流的讀寫,實現(xiàn)文件的存儲;
?????????????? 在下載過程中利用Handler來向外傳遞下載的信息。
???????? 2.斷點續(xù)傳:
?????????????? 對于每一個線程利用一個DownloadInfo類來保存下載的信息,每次在下載過程中向數(shù)據(jù)庫更新信息(我也有想過只在下載暫停時進行更新,但那樣的話我們的進程被殺掉時信息就無法保存下來)。在進行下載之前去訪問數(shù)據(jù)庫是否有記錄存在,如果沒有執(zhí)行第一次下載的初始化,如果存在記錄但下載文件不存在時,刪掉數(shù)據(jù)庫中的記錄之后進行第一次下載的初始化,如果有記錄且文件存在,則從數(shù)據(jù)庫中取出信息。
???????? 實現(xiàn)的效果如圖,自己封裝的類提供了開始,暫停,刪除,以及重新下載的方法。還沒來得及將工程穿上CSDN,給大家一個百度云盤的下載地址:http://pan.baidu.com/s/1dD1Xo8T?
?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ? ?
???????????? 主要類的結(jié)構(gòu)有:
???????????????????? 1)DownloadHttpTool:利用Http協(xié)議進行網(wǎng)絡(luò)下載類
???????????????????? 2)DownlaodSqlTool, DownLoadHelper:數(shù)據(jù)庫相關(guān)操作類
???????????????????? 3)DownloadInfo:下載信息保存類
???????????????????? 4)DownloadUtil:封裝下載方法并提供對外接口的類
?貼一下代碼:DownloadInfo類
package com.example.test; public class DownloadInfo {/*** 保存每個下載線程下載信息類* */private int threadId;// 下載器idprivate int startPos;// 開始點private int endPos;// 結(jié)束點private int compeleteSize;// 完成度private String url;// 下載文件的URL地址public DownloadInfo(int threadId, int startPos, int endPos,int compeleteSize, String url) {this.threadId = threadId;this.startPos = startPos;this.endPos = endPos;this.compeleteSize = compeleteSize;this.url = url;}public DownloadInfo() {}public String getUrl() {return url;}public void setUrl(String url) {this.url = url;}public int getThreadId() {return threadId;}public void setThreadId(int threadId) {this.threadId = threadId;}public int getStartPos() {return startPos;}public void setStartPos(int startPos) {this.startPos = startPos;}public int getEndPos() {return endPos;}public void setEndPos(int endPos) {this.endPos = endPos;}public int getCompeleteSize() {return compeleteSize;}public void setCompeleteSize(int compeleteSize) {this.compeleteSize = compeleteSize;}@Overridepublic String toString() {return "DownloadInfo [threadId=" + threadId + ", startPos=" + startPos+ ", endPos=" + endPos + ", compeleteSize=" + compeleteSize+ "]";} }DownLoadHelper類,創(chuàng)建我們的數(shù)據(jù)庫文件
package com.example.test; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; /*** 利用數(shù)據(jù)庫來記錄下載信息* @author acer*/ public class DownLoadHelper extends SQLiteOpenHelper{private static final String SQL_NAME = "download.db";private static final int DOWNLOAD_VERSION=1;public DownLoadHelper(Context context) {super(context, SQL_NAME, null, DOWNLOAD_VERSION);// TODO Auto-generated constructor stub}/*** 在download.db數(shù)據(jù)庫下創(chuàng)建一個download_info表存儲下載信息*/@Overridepublic void onCreate(SQLiteDatabase db) {db.execSQL("create table download_info(_id integer PRIMARY KEY AUTOINCREMENT, thread_id integer, "+ "start_pos integer, end_pos integer, compelete_size integer,url char)");}@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {} }DownlaodSqlTool進行數(shù)據(jù)的插入更新刪除等操作
package com.example.test; import java.util.ArrayList; import java.util.List; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; /*** * 數(shù)據(jù)庫操作工具類*/ public class DownlaodSqlTool {private DownLoadHelper dbHelper;public DownlaodSqlTool(Context context) {dbHelper = new DownLoadHelper(context);}/*** 創(chuàng)建下載的具體信息*/public void insertInfos(List<DownloadInfo> infos) {SQLiteDatabase database = dbHelper.getWritableDatabase();for (DownloadInfo info : infos) {String sql = "insert into download_info(thread_id,start_pos, end_pos,compelete_size,url) values (?,?,?,?,?)";Object[] bindArgs = { info.getThreadId(), info.getStartPos(),info.getEndPos(), info.getCompeleteSize(), info.getUrl() };database.execSQL(sql, bindArgs);}}/*** 得到下載具體信息*/public List<DownloadInfo> getInfos(String urlstr) {List<DownloadInfo> list = new ArrayList<DownloadInfo>();SQLiteDatabase database = dbHelper.getWritableDatabase();String sql = "select thread_id, start_pos, end_pos,compelete_size,url from download_info where url=?";Cursor cursor = database.rawQuery(sql, new String[] { urlstr });while (cursor.moveToNext()) {DownloadInfo info = new DownloadInfo(cursor.getInt(0),cursor.getInt(1), cursor.getInt(2), cursor.getInt(3),cursor.getString(4));list.add(info);}return list;}/*** 更新數(shù)據(jù)庫中的下載信息*/public void updataInfos(int threadId, int compeleteSize, String urlstr) {SQLiteDatabase database = dbHelper.getWritableDatabase();String sql = "update download_info set compelete_size=? where thread_id=? and url=?";Object[] bindArgs = { compeleteSize, threadId, urlstr };database.execSQL(sql, bindArgs);}/*** 關(guān)閉數(shù)據(jù)庫*/public void closeDb() {dbHelper.close();}/*** 下載完成后刪除數(shù)據(jù)庫中的數(shù)據(jù)*/public void delete(String url) {SQLiteDatabase database = dbHelper.getWritableDatabase();database.delete("download_info", "url=?", new String[] { url });} }DownloadHttpTool進行網(wǎng)絡(luò)下載的類
package com.example.test; import java.io.File; import java.io.InputStream; import java.io.RandomAccessFile; import java.net.HttpURLConnection; import java.net.URL; import java.util.ArrayList; import java.util.List; import android.content.Context; import android.os.Handler; import android.os.Message; import android.util.Log; public class DownloadHttpTool {/*** * 利用Http協(xié)議進行多線程下載具體實踐類*/private static final String TAG = DownloadHttpTool.class.getSimpleName();private int threadCount;//線程數(shù)量private String urlstr;//URL地址private Context mContext;private Handler mHandler;private List<DownloadInfo> downloadInfos;//保存下載信息的類private String localPath;//目錄private String fileName;//文件名private int fileSize;private DownlaodSqlTool sqlTool;//文件信息保存的數(shù)據(jù)庫操作類private enum Download_State {Downloading, Pause, Ready;//利用枚舉表示下載的三種狀態(tài)}private Download_State state = Download_State.Ready;//當前下載狀態(tài)private int globalCompelete = 0;//所有線程下載的總數(shù)public DownloadHttpTool(int threadCount, String urlString,String localPath, String fileName, Context context, Handler handler) {super();this.threadCount = threadCount;this.urlstr = urlString;this.localPath = localPath;this.mContext = context;this.mHandler = handler;this.fileName = fileName;sqlTool = new DownlaodSqlTool(mContext);}//在開始下載之前需要調(diào)用ready方法進行配置public void ready() {Log.w(TAG, "ready");globalCompelete = 0;downloadInfos = sqlTool.getInfos(urlstr);if (downloadInfos.size() == 0) {initFirst();} else {File file = new File(localPath + "/" + fileName);if (!file.exists()) {sqlTool.delete(urlstr);initFirst();} else {fileSize = downloadInfos.get(downloadInfos.size() - 1).getEndPos();for (DownloadInfo info : downloadInfos) {globalCompelete += info.getCompeleteSize();}Log.w(TAG, "globalCompelete:::" + globalCompelete);}}}public void start() {Log.w(TAG, "start");if (downloadInfos != null) {if (state == Download_State.Downloading) {return;}state = Download_State.Downloading;for (DownloadInfo info : downloadInfos) {Log.v(TAG, "startThread");new DownloadThread(info.getThreadId(), info.getStartPos(),info.getEndPos(), info.getCompeleteSize(),info.getUrl()).start();}}}public void pause() {state = Download_State.Pause;sqlTool.closeDb();}public void delete(){compelete();File file = new File(localPath + "/" + fileName);file.delete();}public void compelete() {sqlTool.delete(urlstr);sqlTool.closeDb();}public int getFileSize() {return fileSize;}public int getCompeleteSize() {return globalCompelete;}//第一次下載初始化private void initFirst() {Log.w(TAG, "initFirst");try {URL url = new URL(urlstr);HttpURLConnection connection = (HttpURLConnection) url.openConnection();connection.setConnectTimeout(5000);connection.setRequestMethod("GET");fileSize = connection.getContentLength();Log.w(TAG, "fileSize::" + fileSize);File fileParent = new File(localPath);if (!fileParent.exists()) {fileParent.mkdir();}File file = new File(fileParent, fileName);if (!file.exists()) {file.createNewFile();}// 本地訪問文件RandomAccessFile accessFile = new RandomAccessFile(file, "rwd");accessFile.setLength(fileSize);accessFile.close();connection.disconnect();} catch (Exception e) {e.printStackTrace();}int range = fileSize / threadCount;downloadInfos = new ArrayList<DownloadInfo>();for (int i = 0; i < threadCount - 1; i++) {DownloadInfo info = new DownloadInfo(i, i * range, (i + 1) * range- 1, 0, urlstr);downloadInfos.add(info);}DownloadInfo info = new DownloadInfo(threadCount - 1, (threadCount - 1)* range, fileSize - 1, 0, urlstr);downloadInfos.add(info);sqlTool.insertInfos(downloadInfos);}//自定義下載線程private class DownloadThread extends Thread {private int threadId;private int startPos;private int endPos;private int compeleteSize;private String urlstr;private int totalThreadSize;public DownloadThread(int threadId, int startPos, int endPos,int compeleteSize, String urlstr) {this.threadId = threadId;this.startPos = startPos;this.endPos = endPos;totalThreadSize = endPos - startPos + 1;this.urlstr = urlstr;this.compeleteSize = compeleteSize;}@Overridepublic void run() {HttpURLConnection connection = null;RandomAccessFile randomAccessFile = null;InputStream is = null;try {randomAccessFile = new RandomAccessFile(localPath + "/"+ fileName, "rwd");randomAccessFile.seek(startPos + compeleteSize);URL url = new URL(urlstr);connection = (HttpURLConnection) url.openConnection();connection.setConnectTimeout(5000);connection.setRequestMethod("GET");connection.setRequestProperty("Range", "bytes="+ (startPos + compeleteSize) + "-" + endPos);is = connection.getInputStream();byte[] buffer = new byte[1024];int length = -1;while ((length = is.read(buffer)) != -1) {randomAccessFile.write(buffer, 0, length);compeleteSize += length;Message message = Message.obtain();message.what = threadId;message.obj = urlstr;message.arg1 = length;mHandler.sendMessage(message);sqlTool.updataInfos(threadId, compeleteSize, urlstr);Log.w(TAG, "Threadid::" + threadId + " ? ?compelete::"+ compeleteSize + " ? ?total::" + totalThreadSize);if (compeleteSize >= totalThreadSize) {break;}if (state != Download_State.Downloading) {break;}}} catch (Exception e) {e.printStackTrace();} finally {try {if (is != null) {is.close();}randomAccessFile.close();connection.disconnect();} catch (Exception e) {e.printStackTrace();}}}} }DownloadUtils提供下載向外接口方法類:
package com.example.test; import android.annotation.SuppressLint; import android.content.Context; import android.os.AsyncTask; import android.os.Handler; import android.os.Message; import android.util.Log; /*** 將下載方法封裝在此類* 提供下載,暫停,刪除,以及重置的方法*/ public class DownloadUtil {private DownloadHttpTool mDownloadHttpTool;private OnDownloadListener onDownloadListener;private int fileSize;private int downloadedSize = 0;@SuppressLint("HandlerLeak")private Handler mHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {// TODO Auto-generated method stubsuper.handleMessage(msg);int length = msg.arg1;synchronized (this) {//加鎖保證已下載的正確性downloadedSize += length;}if (onDownloadListener != null) {onDownloadListener.downloadProgress(downloadedSize);}if (downloadedSize >= fileSize) {mDownloadHttpTool.compelete();if (onDownloadListener != null) {onDownloadListener.downloadEnd();}}}};public DownloadUtil(int threadCount, String filePath, String filename,String urlString, Context context) {mDownloadHttpTool = new DownloadHttpTool(threadCount, urlString,filePath, filename, context, mHandler);}//下載之前首先異步線程調(diào)用ready方法獲得文件大小信息,之后調(diào)用開始方法public void start() {new AsyncTask<Void, Void, Void>() {@Overrideprotected Void doInBackground(Void... arg0) {// TODO Auto-generated method stubmDownloadHttpTool.ready();return null;}@Overrideprotected void onPostExecute(Void result) {// TODO Auto-generated method stubsuper.onPostExecute(result);fileSize = mDownloadHttpTool.getFileSize();downloadedSize = mDownloadHttpTool.getCompeleteSize();Log.w("Tag", "downloadedSize::" + downloadedSize);if (onDownloadListener != null) {onDownloadListener.downloadStart(fileSize);}mDownloadHttpTool.start();}}.execute();}public void pause() {mDownloadHttpTool.pause();}public void delete(){mDownloadHttpTool.delete();}public void reset(){mDownloadHttpTool.delete();start();}public void setOnDownloadListener(OnDownloadListener onDownloadListener) {this.onDownloadListener = onDownloadListener;}//下載回調(diào)接口public interface OnDownloadListener {public void downloadStart(int fileSize);public void downloadProgress(int downloadedSize);//記錄當前所有線程下總和public void downloadEnd();} }在MainActivity
package com.example.test; import android.os.Bundle; import android.os.Environment; import android.support.v4.app.FragmentActivity; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.ProgressBar; import android.widget.TextView; import com.example.test.DownloadUtil.OnDownloadListener; public class MainActivity extends FragmentActivity {private static final String TAG = MainActivity.class.getSimpleName();private ProgressBar mProgressBar;private Button start;private Button pause;private Button delete;private Button reset;private TextView total;private int max;private DownloadUtil mDownloadUtil;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mProgressBar = (ProgressBar) findViewById(R.id.progressBar1);start = (Button) findViewById(R.id.button_start);pause = (Button) findViewById(R.id.button_pause);delete = (Button) findViewById(R.id.button_delete);reset = (Button) findViewById(R.id.button_reset);total = (TextView) findViewById(R.id.textView_total);String urlString = "http://bbra.cn/Uploadfiles/imgs/20110303/fengjin/013.jpg";String localPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/local";mDownloadUtil = new DownloadUtil(2, localPath, "abc.jpg", urlString,this);mDownloadUtil.setOnDownloadListener(new OnDownloadListener() {@Overridepublic void downloadStart(int fileSize) {// TODO Auto-generated method stubLog.w(TAG, "fileSize::" + fileSize);max = fileSize;mProgressBar.setMax(fileSize);}@Overridepublic void downloadProgress(int downloadedSize) {// TODO Auto-generated method stubLog.w(TAG, "Compelete::" + downloadedSize);mProgressBar.setProgress(downloadedSize);total.setText((int) downloadedSize * 100 / max + "%");}@Overridepublic void downloadEnd() {// TODO Auto-generated method stubLog.w(TAG, "ENd");}});start.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View arg0) {// TODO Auto-generated method stubmDownloadUtil.start();}});pause.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View arg0) {// TODO Auto-generated method stubmDownloadUtil.pause();}});delete.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View arg0) {// TODO Auto-generated method stubmDownloadUtil.delete();}});reset.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View arg0) {// TODO Auto-generated method stubmDownloadUtil.reset();}});} }轉(zhuǎn)載于:https://blog.51cto.com/kenkao/1659986
總結(jié)
以上是生活随笔為你收集整理的【Android】多线程下载加断点续传的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ViewPager用法
- 下一篇: 泛型的通配符