Android開發--多線程下載加斷點續傳

來源:互聯網
上載者:User

標籤:android開發   網路   多線程   下載   斷點續傳   

        檔案下載在App應用中也用到很多,一般版本更新時多要用的檔案下載來進行處理,以前也有看過很多大神有過該方面的部落格,今天我也自己來實踐一下,寫的一般,還請大家多提意見,共同進步。主要思路:

        1.多線程下載:

               首先通過下載匯流排程數來劃分檔案的下載地區:利用int range = fileSize / threadCount;得到每一段下載量;每一段的位置是i * range到(i + 1) * rang  - 1,注意最後一段的位置是到filesize - 1;

               通過Http協議的Range欄位實現下載檔案的分段;

               通過Java類RandomAccessFile可以實現檔案的隨機訪問,利用seek方法定位的檔案的指定位置;

               由HttpUrlConnection擷取流來進行流的讀寫,實現檔案的儲存;

               在下載過程中利用Handler來向外傳遞下載的資訊。

         2.斷點續傳:

               對於每一個線程利用一個DownloadInfo類來儲存下載的資訊,每次在下載過程中向資料庫更新資訊(我也有想過只在下載暫停時進行更新,但那樣的話我們的進程被殺掉時資訊就無法儲存下來)。在進行下載之前去訪問資料庫是否有記錄存在,如果沒有執行第一次下載的初始化,如果存在記錄但下載檔案不存在時,刪掉資料庫中的記錄之後進行第一次下載的初始化,如果有記錄且檔案存在,則從資料庫中取出資訊。

         實現的效果,自己封裝的類提供了開始,暫停,刪除,以及重新下載的方法。還沒來得及將工程穿上CSDN,給大家一個百度雲端硬碟的:http://pan.baidu.com/s/1dD1Xo8T 

                                                                                                             

             主要類的結構有:

                     1)DownloadHttpTool:利用Http協議進行網路下載類

                     2)DownlaodSqlTool, DownLoadHelper:資料庫相關操作類

                     3)DownloadInfo:下載資訊儲存類

                     4)DownloadUtil:封裝下載方法並提供對外介面的類

 貼一下代碼:DownloadInfo類

package com.example.test;public class DownloadInfo {/** * 儲存每個下載線程下載資訊類 *  */private int threadId;// 下載器idprivate int startPos;// 開始點private int endPos;// 結束點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類,建立我們的資料庫檔案
package com.example.test;import android.content.Context;import android.database.sqlite.SQLiteDatabase;import android.database.sqlite.SQLiteOpenHelper;/** * 利用資料庫來記錄下載資訊 * @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資料庫下建立一個download_info表格儲存體下載資訊     */    @Override    public 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)");    }    @Override    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {    }}
DownlaodSqlTool進行資料的插入更新刪除等操作
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;/** *  * 資料庫操作工具類 */public class DownlaodSqlTool {    private DownLoadHelper dbHelper;    public DownlaodSqlTool(Context context) {        dbHelper = new DownLoadHelper(context);    }    /**     * 建立下載的具體資訊     */    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;    }    /**     * 更新資料庫中的下載資訊     */    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);    }    /**     * 關閉資料庫     */    public void closeDb() {        dbHelper.close();    }    /**     * 下載完成後刪除資料庫中的資料     */    public void delete(String url) {        SQLiteDatabase database = dbHelper.getWritableDatabase();        database.delete("download_info", "url=?", new String[] { url });    }}
DownloadHttpTool進行網路下載的類
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協議進行多線程下載具體實踐類 */private static final String TAG = DownloadHttpTool.class.getSimpleName();private int threadCount;//線程數量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;//檔案資訊儲存的資料庫操作類private enum Download_State {Downloading, Pause, Ready;//利用枚舉表示下載的三種狀態}private Download_State state = Download_State.Ready;//當前下載狀態private int globalCompelete = 0;//所有線程下載的總數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);}//在開始下載之前需要調用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);}//下載之前首先非同步線程調用ready方法獲得檔案大小資訊,之後調用開始方法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;}//下載回調介面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();}});}}






Android開發--多線程下載加斷點續傳

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.