日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

android多线程下载原理,安卓多线程断点续传下载功能(靠谱第三方组件,原理demo)...

發(fā)布時間:2025/5/22 编程问答 20 豆豆
生活随笔 收集整理的這篇文章主要介紹了 android多线程下载原理,安卓多线程断点续传下载功能(靠谱第三方组件,原理demo)... 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

一,原生的DownloadManager

從Android 2.3(API level 9)開始,Android以Service的方式提供了全局的DownloadManager來系統(tǒng)級地優(yōu)化處理長時間的下載操作。

DownloadManager支持失敗重試、Notification通知等基本特性。但是DownloadManager提供的接口很有限,雖然有暫停狀態(tài)卻沒有提供主動暫停的接口。所以需手動實現(xiàn)斷點續(xù)傳,以及需手動實現(xiàn)單文件的多線程(分段)下載,還是比較麻煩。

總結(jié):DownloadManager比較適用于簡單的單文件下載。

二,第三方組件

1,FileDownloader

git地址:https://github.com/lingochamp/FileDownloader

中文文檔:https://github.com/lingochamp/FileDownloader/blob/master/README-zh.md

描述:在第三方下載組件中比較突出,成熟,穩(wěn)定,健壯。支持多任務下載,支持多線程下載,支持斷點續(xù)傳,支持替換網(wǎng)絡請求框架等。

實際應用:在快手的后臺進程里有l(wèi)iulishuo/FileDownloader,因為快手內(nèi)有下載服務,有可能用于應用內(nèi)的更新模塊,liulishuo的FileDownloader就是這個組件。

image

總結(jié):建議在項目中實現(xiàn)多任務多線程下載功能優(yōu)先考慮上面的第三方組件,而不是重復造輪子。畢竟能在受眾群大的商業(yè)應用中成熟使用的第三方框架,是經(jīng)過多次迭代的思想結(jié)晶,考慮了多種情況,踩過很多坑。一般自己從頭寫的話,很難比其的做得更好,考慮得更完善,尤其是在有限的項目開發(fā)時間內(nèi)。

三,原理demo

這里列一個兩年前寫的demo,用的都是非常基礎的東西。考慮的點也比較少,就只是從頭實現(xiàn)這個功能。

(一)思路:

1,如何實現(xiàn)多線程下載功能?

(后續(xù)都假設1個文件啟動3個線程去下載)先起一個初始線程(initThread)去查詢目標下載文件的基本信息比如文件長度等,生成對應本地文件。計算每個下載線程(DownloadThread)的下載長度和下載起始結(jié)束點,通過線程池啟動3個下載線程進行下載并寫入到本地文件的對應起始點中,當3個下載線程都下載完成后,這個文件就下載完整了。

2,如何實現(xiàn)暫停功能?

設計一個下載任務類(DownloadTask),開始下載一個文件就新建一個下載任務類DownloadTask。下載任務類負責管理啟動多個下載線程開始下載。下載任務類里設置一個公開的暫停標志,在外部可以設置暫停標志的true/false,當下載線程下載文件時,判斷到暫停標志為true,則結(jié)束本線程即中斷下載。

3,如何實現(xiàn)即時展示當前下載進度?

下載任務類里設置一個表示當前已完成下載長度的變量,在下載線程的下載過程中,及時更新累加。設置每500ms計算一次下載進度=當前已完成下載長度/下載文件總長度,并發(fā)出廣播。外部接收到廣播后更新ui。

4,如何實現(xiàn)斷點續(xù)傳功能(暫停/網(wǎng)絡故障后再恢復下載從之前下載進度基礎上繼續(xù)下載,而不是重新下載)?

這就必須把下載進程的相關(guān)信息本地存儲,然后在恢復下載時查詢本地存儲的數(shù)據(jù),獲得目前的下載進度,再從那開始下載。

要給每一個下載線程保存一條這個線程相關(guān)信息的數(shù)據(jù),考慮到當新建n個下載任務時,同時開啟3n個下載線程,就需要保存3n條線程信息數(shù)據(jù)。而在暫停/網(wǎng)絡故障/下載失敗時,都要更新對應數(shù)據(jù),選用本地數(shù)據(jù)庫sqlite來存儲數(shù)據(jù)。

安卓的數(shù)據(jù)表的操作,基本就是“數(shù)據(jù)模型+helper幫助類+dao實現(xiàn)類”來一套。

但是注意這里是多線程操作數(shù)據(jù)表,“增刪改查”操作里,“增刪改”方法都要加上synchronized修飾詞。synchronized是保證線程安全,就是在當多個線程操作同一資源時保證某一時刻只能有一個線程在執(zhí)行這個任務。

舉個例子,想象一個線程是一個人,一張數(shù)據(jù)表裝在一棟房子里,房子有兩個門,門a只給不帶synchronized方法的人進,門b只給帶synchronized方法的人進。門a沒什么條件,只要是使用不帶synchronized方法的人都可以直接進去。門b卻有條件,一次只能進去一個人,并且下一個進去的人要在上一個進去的人出來之后才能進去。

(二)當年湊zhuanli畫的流程圖:

image

image

(三)代碼:

啟動下載

Intent intent = new Intent( mContext, DownloadService.class );

fileInfo.setIsDownload("true");

intent.setAction(IntentAction.ACTION_START);

intent.putExtra( KeyName.FILEINFO_TAG, fileInfo );

mContext.startService(intent); // 通過intent傳遞信息fileInfo給servers

暫停下載

Intent intent = new Intent( mContext, DownloadService.class );

fileInfo.setIsDownload("false");

intent.setAction(IntentAction.ACTION_PAUSE);

intent.putExtra( KeyName.FILEINFO_TAG, fileInfo );

mContext.startService(intent);

DownloadService

/**

* 用于文件下載的service

*/

public class DownloadService extends Service {

/**

* 定義消息處理handler的標志

*/

private static final int MSG_INIT =0 ;

/**

* 文件下載線程任務的集合

*/

private MapmTask =new LinkedHashMap();

/**

* onStartCommand()接收Activity中StartService發(fā)送的信息

*/

@Override

public int onStartCommand(Intent intent, int flags, int startId) {

// 獲取Activity傳進來的參數(shù)

FileInfo fileInfo =(FileInfo) intent.getSerializableExtra( KeyName.FILEINFO_TAG );// 獲取下載文件信息

Log.i("intent", "DownloadService-47行- "+fileInfo.getFileName()+" intent=null: " + (intent==null) );

Log.i("intent", "DownloadService-48行- "+fileInfo.getFileName()+" intent.getSerializableExtra(Config.FILEINFO_TAG):"

+(intent.getSerializableExtra(KeyName.FILEINFO_TAG)==null) );

// 開啟多線程下載

if ( IntentAction.ACTION_START.equals(intent.getAction()) ) {

initThread mInitThread =new initThread(fileInfo);

DownloadTask.sExecutorService.execute(mInitThread);// 通過線程池開啟初始化線程

// 暫停多線程下載

}else if ( IntentAction.ACTION_PAUSE.equals(intent.getAction()) ) {

DownloadTask tast =mTask.get(fileInfo.getId() ); // 從集合中取出下載任務

if (tast !=null) {

tast.isPause =true ;

}

}else if(IntentAction.ACTION_DELETE.equals(intent.getAction())){

DownloadTask tast =mTask.get(fileInfo.getId() ); // 從集合中取出下載任務

if (tast !=null) {

tast.isDel=true ;

}

}

return super.onStartCommand(intent, flags, startId);

}

@Override

public IBinderonBind(Intent intent) {

return null;

}

/**

* 初始化文件下載線程:創(chuàng)建本地下載位置,并開啟下載任務

**/

class initThread extends Thread{

/**

* 下載的文件的所有屬性信息

*/

private FileInfomFileInfo =null ;

/**

* 初始化文件下載線程:確保創(chuàng)建本地下載位置,并開啟文件下載任務

* @param mFileInfo 下載的文件的所有屬性信息

*/

public initThread(FileInfo mFileInfo) {

this.mFileInfo = mFileInfo;

}

//開啟開啟下載任務

Handlerhandler =new Handler(){

@Override

public void handleMessage(android.os.Message msg) {

switch (msg.what) {

case MSG_INIT:

FileInfo fileInfo = (FileInfo) msg.obj ;

// 啟動下載任務

DownloadTask task =new DownloadTask( DownloadService.this, fileInfo, DownloadConfig.DONWNLOAD_THREAD_NUM);

task.download();

//把下載任務添加到下載集合中

mTask.put(fileInfo.getId(), task); //將開啟下載線程的id和實例添加到map中,在暫停時通過id獲取實例,并令它暫停

break;

default:

break;

}

}

};

@Override

public void run(){

HttpURLConnection conn =null ;

RandomAccessFile raf =null ;// 隨機訪問文件,可以在文件的隨機寫入,對應斷點續(xù)傳功能

try {

// 連接網(wǎng)絡文件

URL url =new URL(mFileInfo.getUrl() );

conn = (HttpURLConnection) url.openConnection();

conn.setConnectTimeout(3000);// 設置連接超時時間

conn.setRequestMethod("GET");

// 獲取文件長度

int length = -1;

if (conn.getResponseCode()== HttpStatus.SC_OK ) {

length = conn.getContentLength();

Log.i("http", "DownloadService-138行 獲取網(wǎng)絡數(shù)據(jù)長度="+ length );

}else{

Log.i("http", "DownloadService-140行 http連接失敗!");

}

if ( length <=0 ){

return ;

}

// 判斷下載路徑是否存在

File dir =new File( DownloadConfig.DOWNLOAD_PATH );

if ( !dir.exists() ){

dir.mkdir();

}

// 在本地創(chuàng)建文件

File file =new File( dir, mFileInfo.getFileName() );

raf =new RandomAccessFile( file, "rwd" );

// 設置文件長度

raf.setLength( length );

mFileInfo.setLength( length );

handler.obtainMessage(MSG_INIT, mFileInfo).sendToTarget(); //將數(shù)據(jù)發(fā)回給handler

//關(guān)閉連接

if (raf!=null) {

raf.close();

}if (conn !=null) {

conn.disconnect();

}

}catch (Exception e) {

e.printStackTrace();

}

}

}

}

DownloadTask

/**

* 下載任務類

**/

public class DownloadTask {

/**

* 上下文

*/

private Context mContext = null ;

/**

* 下載的文件的所有屬性信息

*/

private FileInfo mFileInfo = null ;

/**

* 數(shù)據(jù)(庫)訪問接口

*/

private ThreadDAO mDAO = null;

/**

* 已下載字節(jié)長度

*/

private int mFinishedLen = 0;

/**

* 下載暫停標志

*/

public Boolean isPause = false;

/**

* 下載刪除標志

*/

public Boolean isDel = false;

/**

* 默認的下載線程數(shù)

*/

private int mThreadNum = 0 ;

/**

* 下載線程集合

*/

private List mThreadList = null ;

/**

* 帶緩存線程池,s開頭表示用到static關(guān)鍵字

*/

public static ExecutorService sExecutorService = Executors.newCachedThreadPool();

/**

* 文件下載的線程任務類

* @param mContext 上下文

* @param mFileInfo 下載的文件的所有屬性信息

* @param threadNum 文件分段下載線程數(shù)

*/

public DownloadTask(Context mContext, FileInfo mFileInfo, int threadNum) {

this.mContext = mContext;

this.mFileInfo = mFileInfo;

this.mThreadNum = threadNum ;

mDAO = new ThreadDAOIImpl(mContext);

}

/**

* 開始下載文件

*/

public void download() {

List threadInfoList = mDAO.getThreads(mFileInfo.getUrl()); // 讀取數(shù)據(jù)庫中文件下載的信息

// 不存下載線程則創(chuàng)建

if( threadInfoList.size()== 0 ){

int length = mFileInfo.getLength()/mThreadNum ; // 獲取單個線程下載長度

for (int i = 0; i < mThreadNum; i++) { // 創(chuàng)建線程信息

ThreadInfo threadInfo = new ThreadInfo(i, mFileInfo.getUrl(),length*i, (i+1)*length-1, 0, mFileInfo.getMd5(),"false", "none");

if (i==mThreadNum-1) {

threadInfo.setEnd(mFileInfo.getLength());// 設置最后一個線程的下載長度

}

threadInfoList.add(threadInfo); // 添加線程信息到集合

mDAO.insertThread(threadInfo); // 向數(shù)據(jù)庫插入文件下載的信息

} // 放到run外面,比較不容易產(chǎn)生數(shù)據(jù)庫的死鎖

}

// 啟動多個線程進行下載

mThreadList = new ArrayList();

for (ThreadInfo info : threadInfoList) {

DownloadThread downloadThread = new DownloadThread(info);

if ( info.getFinished()

info.getFinished()

DownloadTask.sExecutorService.execute(downloadThread);

mThreadList.add(downloadThread); //添加線程到集合中

}

}

}

/**

* 判斷所有線程是否執(zhí)行完成

* @param threadInfo 下載文件的線程信息

*/

private synchronized void checkAllThreadsFinished(ThreadInfo threadInfo){ // synchronized 同步方法,保證同一時間只有一個線程訪問該方法

boolean allFinished = true ; //所有線程下載結(jié)束標識

// 遍歷線程集合,判斷是否都下載完畢

for (DownloadThread thread : mThreadList) {

if (!thread.isFinished) {

allFinished = false ;

break ;

}

}

// 所有線程下載結(jié)束:驗證md5,相同則存儲下載長度,否則清空;發(fā)送廣播通知UI下載任務結(jié)束

if (allFinished) {

MD5Util md5Util = new MD5Util();

DateTools dateTools = new DateTools();

List threadInfosList =mDAO.getThreads(threadInfo.getUrl());

for (ThreadInfo info :threadInfosList) {

if ( !md5Util.isMD5Equal(info) ) { // md5驗證失敗,清空下載長度

mDAO.updateThread(info.getUrl(), info.getId(), 0, info.getMd5(), "false" ,dateTools.getCurrentTime() );

}else if(info.getId()==0) {

mDAO.updateThread(info.getUrl(), info.getId(),(info.getEnd()-info.getStart()), info.getMd5(), "true", dateTools.getCurrentTime() );

}else if(info.getId()!=0){

mDAO.updateThread(info.getUrl(), info.getId(),(info.getEnd()-info.getStart()+1), info.getMd5(), "true",dateTools.getCurrentTime() );

}

}

Intent intent = new Intent(IntentAction.ACTION_FINISH);

mFileInfo.setOvertime(dateTools.getCurrentTime());

intent.putExtra( KeyName.FILEINFO_TAG, mFileInfo);

mContext.sendBroadcast(intent);

}

}

/**

* 進行文件下載的線程

**/

class DownloadThread extends Thread{

/**

* 文件下載線程的信息

*/

private ThreadInfo mThreadInfo = null ;

/**

* 標識線程是否執(zhí)行完成

*/

public Boolean isFinished = false ;

/**

* 廣播下載進度的間隔時間

*/

private final static int BROADCAST_TIME = 100 ;

/**

* httpUrl連接

*/

HttpURLConnection conn = null ;

/**

* 任意寫入文件:RandomAccessFile

*/

RandomAccessFile raf= null;

/**

* 輸入流

*/

InputStream input = null ;

/**

* 進行文件下載的線程

* @param mThreadInfo 文件下載線程的信息

**/

public DownloadThread(ThreadInfo mThreadInfo) {

this.mThreadInfo = mThreadInfo;

}

@Override

public void run(){

try {

URL url = new URL(mThreadInfo.getUrl());

conn = (HttpURLConnection) url.openConnection();

conn.setConnectTimeout(3000);

conn.setRequestMethod("GET");

//設置url上資源下載位置/范圍

int start = mThreadInfo.getStart() +mThreadInfo.getFinished();

conn.setRequestProperty("Range", "bytes=" +start +"-" + mThreadInfo.getEnd() );

//設置文件本地寫入位置

File file = new File( DownloadConfig.DOWNLOAD_PATH, mFileInfo.getFileName() );

raf = new RandomAccessFile(file, "rwd");

raf.seek(start);

mFinishedLen +=mThreadInfo.getFinished();

//開始下載啦

if ( conn.getResponseCode() == HttpStatus.SC_PARTIAL_CONTENT ){

//讀取數(shù)據(jù)

input = conn.getInputStream();

byte[] buffer = new byte[ 1024 * 4 ];

int len = -1;

long time = System.currentTimeMillis();

while((len = input.read(buffer))!=-1){

//寫入文件

raf.write(buffer,0,len);

mFinishedLen += len ;

mThreadInfo.setFinished(mThreadInfo.getFinished() + len);

if (System.currentTimeMillis()-time > BROADCAST_TIME ) { //每500ms刷新一次

time = System.currentTimeMillis();

double progress_result = (double)mFinishedLen / (double)mFileInfo.getLength(); // 計算下載進度

double download_rate = (double)len / (double)(1024*BROADCAST_TIME/1000); // 計算下載速率

Intent intent = new Intent( IntentAction.ACTION_UPDATE);

intent.putExtra( KeyName.FINISHED_TAG,progress_result );

intent.putExtra( KeyName.DOWNLOAD_RATE_TAG,download_rate );

intent.putExtra("id", mFileInfo.getId() );

mContext.sendBroadcast(intent);

}

//下載暫停時,保存下載進度搭配數(shù)據(jù)庫

if (isPause) {

mDAO.updateThread(mThreadInfo.getUrl(), mThreadInfo.getId(), mThreadInfo.getFinished(), mThreadInfo.getMd5(), "false", "none" );

return;

}

//刪除下載時,刪除數(shù)據(jù)庫內(nèi)的相關(guān)信息

if (isDel) {

if(!mDAO.getThreads(mThreadInfo.getUrl()).isEmpty()) {

mDAO.deleteThread(mThreadInfo.getUrl());

}

// Intent intent = new Intent( IntentAction.ACTION_DELETE);

// mContext.sendBroadcast(intent);

return;

}

// 下載連接獲取輸入流或文件寫入失敗,清空文件下載進度,并重新下載

if (raf ==null || input ==null ){

mDAO.updateThread(mThreadInfo.getUrl(), 0, 0, mThreadInfo.getMd5(), "false" , "none");

mDAO.updateThread(mThreadInfo.getUrl(), 1, 0, mThreadInfo.getMd5(), "false" , "none");

mDAO.updateThread(mThreadInfo.getUrl(), 2, 0, mThreadInfo.getMd5(), "false" , "none");

Intent intent = new Intent( IntentAction.ACTION_UPDATE);

intent.putExtra( KeyName.FINISHED_TAG, 0 );

intent.putExtra( KeyName.DOWNLOAD_RATE_TAG, 0 );

intent.putExtra("id", mFileInfo.getId() );

mContext.sendBroadcast(intent);

download();

}

}

isFinished = true ;// 標識線程執(zhí)行完畢

// 檢查下載任務是否執(zhí)行完畢

checkAllThreadsFinished(mThreadInfo );

}

} catch (Exception e) {

e.printStackTrace();

}finally{//關(guān)閉連接

try {

if (raf !=null) {

raf.close();

}else {

System.out.println("DOwnloadTask-280行 RadomAccessFile發(fā)生錯誤");

}if (input !=null) {

input.close();

}else {

System.out.println("DOwnloadTask-280行 inputStream發(fā)生錯誤");

}if (conn !=null) {

conn.disconnect();

}else {

System.out.println("DOwnloadTask-280行 HttpURLConnection發(fā)生錯誤");

}

} catch (IOException e) {

e.printStackTrace();

}

}

}

}

}

FileInfo

/**

* 下載的文件的所有屬性信息

*/

public class FileInfo implements Serializable{

private static final long serialVersionUID = 1L;

/**

* 下載文件的id

*/

private int id ;

/**

* 下載文件總長度

*/

private int length ;

/**

* 下載文件的web路徑

*/

private String url ;

/**

* 下載文件名

*/

private String fileName ;

/**

* 下載文件的 MD5值

*/

private String md5 ;

/**

* 文件下載進度(%)

*/

private double finished ;

/**

* 文件下載速率

*/

private double rate ;

/**

* 下載文件的 MD5值

*/

private String over ;

/**

* 下載文件完成下載的時間

*/

private String overtime ;

public String getOvertime() {

return overtime;

}

public void setOvertime(String overtime) {

this.overtime = overtime;

}

/**

* 下載文件是否正在下載

*/

private String isDownload ;

public FileInfo() {

}

/**

* 存儲下載文件的屬性信息

* @param id 下載文件的id標識

* @param length 文件總長度

* @param url 文件web路徑

* @param fileName 下載文件名

* @param finished 文件下載進度

* @param rate 文件下載速率

* @param md5 文件的md5值

* @param over 文件是否完成下載

* @param isDownload 文件是否正在下載

*/

public FileInfo(int id, int length, String url, String fileName,

int finished,double rate, String md5, String over, String isDownload, String overtime) {

this.id = id;

this.url = url;

this.length = length;

this.fileName = fileName;

this.finished = finished;

this.rate = rate;

this.md5 = md5;

this.over = over ;

this.overtime = overtime ;

this.isDownload = isDownload ;

}

/**

* 獲取文件下載狀態(tài)

*/

public String getIsDownload() {

return isDownload;

}

/**

* 設置文件下載狀態(tài)

* @param isDownload 文件是否正在下載

*/

public void setIsDownload(String isDownload) {

this.isDownload = isDownload;

}

/**

* 獲取文件下載進度(%)

*/

public double getRate() {

return rate;

}

/**

* 存儲文件下載進度(%)

*/

public void setRate(double rate) {

this.rate = rate;

}

/**

* 獲取文件MD5

*/

public String getMd5() {

return md5;

}

/**

* 存儲文件MD5

*/

public void setMd5(String md5) {

this.md5 = md5;

}

/**

* 獲取下載文件的id標識

*/

public int getId() {

return id;

}

/**

* 設置下載文件的

*/

public void setId(int id) {

this.id = id;

}

/**

* 獲取下載文件的長度

*/

public int getLength() {

return length;

}

/**

* 存入下載文件的

*/

public void setLength(int length) {

this.length = length;

}

/**

* 獲取下載文件的URL路徑

*/

public String getUrl() {

return url;

}

/**

* 存儲下載文件的URL路徑

*/

public void setUrl(String url) {

this.url = url;

}

/**

* 獲取下載文件的文件名

*/

public String getFileName() {

return fileName;

}

/**

* 存儲下載文件的文件名

*/

public void setFileName(String fileName) {

this.fileName = fileName;

}

/**

* 獲取下載文件的下載進度

*/

public double getFinished() {

return finished;

}

/**

* 存儲下載文件的存儲進度

*/

public void setFinished(double finished) {

this.finished = finished;

}

/**

* 獲取文件完成下載標識

*/

public String getOver() {

return over;

}

/**

* 存儲文件完成下載標識

*/

public void setOver(String over) {

this.over = over;

}

/**

* 獲取下載文件的所有屬性信息

*/

@Override

public String toString() {

return "FileInfo [id=" + id + ", length=" + length + ", url=" + url

+ ", fileName=" + fileName + ", finished="

+ finished +", rate=" + rate +", md5=" + md5 + "]";

}

}

ThreadInfo

/**

*文件下載線程的信息

**/

public class ThreadInfo {

/**

* 文件下載線程的id標識

*/

private int id ;

/**

* 下載文件的URL路徑

*/

private String url ;

/**

* 文件下載的起始字節(jié)位置

*/

private int start ;

/**

* 文件下載的結(jié)束字節(jié)位置

*/

private int end ;

/**

* 文件已下載的字節(jié)長度

*/

private int finished ;

/**

* 下載文件的 MD5值

*/

private String md5 ;

/**

* 下載文件是否完成下載

*/

private String over ;

/**

* 下載文件完成下載的時間

*/

private String overtime ;

public ThreadInfo() {

}

/**

* 存儲文件下載線程的屬性信息

* @param id 文件下載線程id標識

* @param url 下載文件路徑

* @param start 文件下載起始字節(jié)位置

* @param end 文件下載終止字節(jié)位置

* @param finished 文件已下載的字節(jié)長度

* @param md5 文件的md5值

* @param over 文件是否完成下載

* @param overtime 文件完成下載的時間

*/

public ThreadInfo(int id, String url, int start, int end, int finished, String md5, String over,String overtime) {

this.id = id;

this.url = url;

this.start = start;

this.end = end;

this.finished = finished;

this.md5 = md5;

this.over = over;

this.overtime = overtime;

}

/**

* 獲取文件完成下載的時間

*/

public String getOvertime() {

return overtime;

}

/**

* 設置文件完成下載的時間

*/

public void setOver_time(String overtime) {

this.overtime = overtime;

}

/**

* 獲取文件下載線程的id標識

*/

public int getId() {

return id;

}

/**

* 設置文件下載線程的id標識

*/

public void setId(int id) {

this.id = id;

}

/**

* 獲取文件下載線程的url

*/

public String getUrl() {

return url;

}

/**

* 設置下載文件的url

*/

public void setUrl(String url) {

this.url = url;

}

/**

* 獲取文件下載線程的起始字節(jié)位置

*/

public int getStart() {

return start;

}

/**

* 設置下載文件的起始字節(jié)位置

*/

public void setStart(int start) {

this.start = start;

}

/**

* 獲取文件下載線程的結(jié)束字節(jié)位置

*/

public int getEnd() {

return end;

}

/**

* 設置下載文件的結(jié)束字節(jié)位置

*/

public void setEnd(int end) {

this.end = end;

}

/**

* 獲取文件下載線程已下載的字節(jié)長度

*/

public int getFinished() {

return finished;

}

/**

* 設置文件下載線程已下載的字節(jié)長度

*/

public void setFinished(int finished) {

this.finished = finished;

}

/**

* 獲取文件MD5

*/

public String getMd5() {

return md5;

}

/**

* 存儲文件MD5

*/

public void setMd5(String md5) {

this.md5 = md5;

}

/**

* 獲取文件完成下載標識

*/

public String getOver() {

return over;

}

/**

* 存儲文件完成下載標識

*/

public void setOver(String over) {

this.over = over;

}

/**

* 獲取文件下載線程的所有屬性信息

*/

@Override

public String toString() {

return "ThreadInfo [id=" + id + ", url=" + url + ", start=" + start

+ ", end =" + end + ", finished=" + finished +", md5=" + md5

+ ", over=" + over +"]";

}

}

DBHelper

/**

* 數(shù)據(jù)庫幫助類

* 多線程操作數(shù)據(jù)庫時注意使用【單例模式】

* 1、構(gòu)造方法定為private

* 2、定義該類的一個靜態(tài)對象用以應用

* 3、通過getInstance()方法返回該類對象,

* 使該方法無論調(diào)用多少次,該類都是唯一。

*/

public class DBHelper extends SQLiteOpenHelper{

private AtomicInteger mOpenCounter = new AtomicInteger();

private SQLiteDatabase mDatabase;

/**

* 數(shù)據(jù)庫名稱

*/

private static final String DB_NAME = "download.db" ;

/**

* 數(shù)據(jù)庫名稱

*/

public static final String TABLE_NAME = "thread_info" ;

/**

* 文件下載線程id

*/

public static final String THREAD_ID = "thread_id" ;

/**

* 下載文件的url文件

*/

public static final String URL = "url" ;

/**

* 文件下載的起始位置(字節(jié)長度)

*/

public static final String START = "start" ;

/**

* 文件下載的結(jié)束位置(字節(jié)長度)

*/

public static final String END = "end" ;

/**

* 文件已下載的字節(jié)長度

*/

public static final String FINISHED = "finished" ;

/**

* 文件的md5

*/

public static final String MD5 = "md5" ;

/**

* 文件是否完成下載標識

*/

public static final String OVER = "over" ;

/**

* 文件是成下載時間

*/

public static final String OVER_TIME = "over_time" ;

/**

* 數(shù)據(jù)庫幫助類的靜態(tài)對象引用

*/

private static DBHelper sHelper = null ;

/**

* 數(shù)據(jù)庫版本

*/

private static final int VERSION = 1;

/**

* sql創(chuàng)建保存線程信息表命令句

*/

private static final String SQL_CREATE =

"create table " +TABLE_NAME +" (_id integer primary key autoincrement,"

+ THREAD_ID +" integer, "

+ URL +" text, "

+ START +" integer, "

+ END +" integer,"

+ FINISHED +" integer,"

+ MD5 +" text, "

+ OVER +" text, "

+OVER_TIME+ " text )";

/**

* 刪除表命令句

*/

private static final String SQL_DROP = "drop table if exists "+TABLE_NAME;

private DBHelper(Context context) { // 將public改為private,

super(context, DB_NAME, null, VERSION); // 防止在其它地方被new出來,保證db的單例,防止數(shù)據(jù)庫被鎖定

}

/**

* 獲得類對象sHelper

*/

public static DBHelper getInstance(Context context){ // 單例模式,DBHelper只會被實例化一次

if (sHelper == null ) { // 靜態(tài)方法訪問數(shù)據(jù)庫,無論創(chuàng)建多少個數(shù)據(jù)庫訪問對象,

sHelper = new DBHelper(context); // 里面的Helper只有一個,保證程序中只有一個DBHelper對數(shù)據(jù)庫進行訪問

}

return sHelper ;

}

public synchronized SQLiteDatabase openDatabase() {

if(mOpenCounter.incrementAndGet() == 1) {

// Opening new database

mDatabase = sHelper.getWritableDatabase();

}

return mDatabase;

}

public synchronized void closeDatabase() {

if(mOpenCounter.decrementAndGet() == 0) {

// Closing database

mDatabase.close();

}

}

/**

* 創(chuàng)建表

*/

@Override

public void onCreate(SQLiteDatabase db) {

db.execSQL(SQL_CREATE); //創(chuàng)建表

}

/**

* 更新數(shù)據(jù)庫表

*/

@Override

public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

db.execSQL(SQL_DROP);

db.execSQL(SQL_CREATE);

}

}

ThreadDAO

/**

* 【數(shù)據(jù)訪問接口】

* DAO:data access object 數(shù)據(jù)訪問對象,DAO一定是和數(shù)據(jù)庫的每張表一一對應;

* 是一種應用程序表承接口(api),可以訪問其它的結(jié)構(gòu)化查詢語言(SQL)數(shù)據(jù)庫;

* J2EE用DAO設計模塊,把底層數(shù)據(jù)訪問和高層的商務邏輯分開;典型的DAO有以下幾個組件:

* 1、一個DAO工廠類

* 2、一個DAO接口

* 3、一個實現(xiàn)DAO接口的具體類

* 4、數(shù)據(jù)傳遞對象

*/

public interface ThreadDAO {

/**

* 插入文件下載信息

* @param threadInfo 下載線程的信息

*/

public void insertThread(ThreadInfo threadInfo);

/**

* 刪除文件下載信息

*/

public void deleteThread(String url );

/**

* 更新下載文件下載進度

* @param url 下載線程的URL

* @param thread_id 下載線程的id

* @param finished 下載線程的文件已下載字節(jié)數(shù)

* @param md5 下載文件的md5

* @param over 文件下載完成標識

* @param over_time 文件下載完成時間

*/

public void updateThread(String url, int thread_id, int finished, String md5, String over, String over_time) ;

/**

* 查詢文件的文件下載線程信息

* @param url 下載線程的URL

* @return List 線程信息集

*/

public List getThreads(String url) ;

/**

* 獲取所有文件的文件下載信息

* @return List 包含下載線程信息的list集

*/

public List getDBFileInfoList();

/**

* 文件下載信息是否已結(jié)束下載

* @return 當存在下載信息返回true;否則返回false

*/

boolean isDownloadOver(String url);

}

ThreadDAOIImpl

/**

* 【數(shù)據(jù)庫訪問接口的實現(xiàn)】

*/

public class ThreadDAOIImpl implements ThreadDAO{

/**

* 數(shù)據(jù)庫幫助類

*/

private DBHelper mDBHelper = null ; // 將DBHelper用private修飾

public ThreadDAOIImpl(Context context) {

mDBHelper = DBHelper.getInstance(context);

}

/**

* 插入文件下載信息】

* 多線程數(shù)據(jù)庫的增、刪、改(更新)方法用synchronized修飾,以保證線程安全:

* 保證同一時間段不會有多個(只有一個)線程對數(shù)據(jù)庫進行增刪改,需等待線程執(zhí)

* 行完后再開啟線程執(zhí)行下一個功能;而查詢因為不用操作數(shù)據(jù)庫,不會導致 數(shù)據(jù)庫

* 死鎖 ,所以不用

* @see com.download.db.ThreadDAO#insertThread(com.download.entities.ThreadInfo)

*/

@Override

public synchronized void insertThread(ThreadInfo threadInfo ){

SQLiteDatabase db = mDBHelper.openDatabase(); // 實例化數(shù)據(jù)庫,設置為【讀寫】模式

db.execSQL( "insert into " +DBHelper.TABLE_NAME

+ " ( "+DBHelper.THREAD_ID +","

+DBHelper.URL +","

+DBHelper.START +","

+DBHelper.END +","

+DBHelper.FINISHED +","

+DBHelper.MD5 +","

+DBHelper.OVER +","

+DBHelper.OVER_TIME +")"

+ " values(?,?,?,?,?,?,?,?)",

new Object[] {threadInfo.getId(), threadInfo.getUrl(),

threadInfo.getStart(), threadInfo.getEnd(),

threadInfo.getFinished(),threadInfo.getMd5(),

threadInfo.getOver() ,threadInfo.getOvertime() } // 插入數(shù)據(jù)

);

mDBHelper.closeDatabase(); // 關(guān)閉數(shù)據(jù)庫

}

/**

* 【刪除文件下載信息】

*/

@Override

public synchronized void deleteThread(String url) {

SQLiteDatabase db = mDBHelper.openDatabase();

db.execSQL( "delete from thread_info where url = ?",

new Object[] {url});

mDBHelper.closeDatabase();

}

/**

* 【更新下載文件下載進度】

*/

@Override

public synchronized void updateThread(String url, int thread_id, int finished, String md5, String over, String over_time) {

SQLiteDatabase db = mDBHelper.openDatabase();

db.execSQL( "update " +DBHelper.TABLE_NAME + " set "

+DBHelper.FINISHED+" = ?where " +DBHelper.URL +" =? and "

+DBHelper.THREAD_ID +" =?",

new Object[] {finished, url, thread_id});

db.execSQL( "update " +DBHelper.TABLE_NAME + " set "

+DBHelper.OVER+" = ?where " +DBHelper.URL +" =? and "

+DBHelper.THREAD_ID +" =?",

new Object[] {over, url, thread_id});

db.execSQL("update " + DBHelper.TABLE_NAME + " set "

+ DBHelper.OVER_TIME + " = ?where " + DBHelper.URL + " =? and "

+ DBHelper.THREAD_ID + " =?",

new Object[]{over_time, url, thread_id});

mDBHelper.closeDatabase();

}

/**

* 獲取文件的文件下載信息

* @return List 包含下載線程信息的list集

* @see com.download.db.ThreadDAO#getThreads(java.lang.String)

*/

@Override

public List getThreads(String url) {

SQLiteDatabase db = mDBHelper.getReadableDatabase(); //注意,此處用【只讀】模式

List list = new ArrayList();

Cursor cursor = db.rawQuery("select * from " +DBHelper.TABLE_NAME

+ " where " +DBHelper.URL +" =?", new String[]{url} );

while (cursor.moveToNext()) {

ThreadInfo threadInfo = new ThreadInfo();

threadInfo.setId(cursor.getInt(cursor.getColumnIndex(DBHelper.THREAD_ID)));

threadInfo.setUrl(cursor.getString(cursor.getColumnIndex(DBHelper.URL)));

threadInfo.setStart(cursor.getInt(cursor.getColumnIndex(DBHelper.START)));

threadInfo.setEnd(cursor.getInt(cursor.getColumnIndex(DBHelper.END)));

threadInfo.setFinished(cursor.getInt(cursor.getColumnIndex(DBHelper.FINISHED)));

threadInfo.setMd5(cursor.getString(cursor.getColumnIndex(DBHelper.MD5)));

threadInfo.setOver(cursor.getString(cursor.getColumnIndex(DBHelper.OVER)));

list.add(threadInfo);

}

cursor.close();

db.close();

return list;

}

/**

* 獲取數(shù)據(jù)庫中所有下載文件的信息

* @return List 包含下載線程信息的list集

*/

@Override

public List getDBFileInfoList() {

URLTools urlTools = new URLTools();

List list = new ArrayList();

SQLiteDatabase db = mDBHelper.getReadableDatabase(); //注意,此處用【只讀】模式

//查詢數(shù)據(jù)庫文件下載信息

Cursor cursor = db.rawQuery("select * from " +DBHelper.TABLE_NAME, null );

while( cursor.moveToNext() ){

FileInfo fileInfo = new FileInfo();

if( fileInfo.getId()==0 ){

long finish = 0; //文件已下載的字節(jié)數(shù)

fileInfo.setOver( cursor.getString(cursor.getColumnIndex(DBHelper.OVER)) );

fileInfo.setOvertime( cursor.getString(cursor.getColumnIndex(DBHelper.OVER_TIME)) );

fileInfo.setMd5( cursor.getString(cursor.getColumnIndex(DBHelper.MD5)) );

fileInfo.setUrl( cursor.getString(cursor.getColumnIndex(DBHelper.URL)) );

String fileName = urlTools.getURLFileName( fileInfo.getUrl(), "/" );

fileInfo.setFileName( fileName );

fileInfo.setIsDownload("false");

//獲取百分比下載進度

for (int i = 0; i < DownloadConfig.DONWNLOAD_THREAD_NUM; i++) {

finish += cursor.getInt( cursor.getColumnIndex(DBHelper.FINISHED) );

if ( i < DownloadConfig.DONWNLOAD_THREAD_NUM -1 ){

cursor.moveToPosition( cursor.getPosition() +1 );

}

}

long finished = finish*100/( cursor.getInt(cursor.getColumnIndex(DBHelper.END)) );

fileInfo.setFinished( (int)finished);

list.add(fileInfo);

}

}

cursor.close();

db.close();

return list;

}

/**

* 文件下載是否已完成

* @return 當存在下載信息返回true;否則返回false

*/

@Override

public boolean isDownloadOver(String url) {

SQLiteDatabase db = mDBHelper.getWritableDatabase();

Cursor cursor = db.rawQuery("select * from " +DBHelper.TABLE_NAME

+" where " +DBHelper.URL +" =? and " +DBHelper.THREAD_ID +" = ?",

new String[]{url, 1+"" } );

boolean over = false;

if (cursor.moveToNext()) {

String overStr = cursor.getString(cursor.getColumnIndex(DBHelper.OVER)) ;

try {

String name = URLDecoder.decode(cursor.getString(cursor.getColumnIndex(DBHelper.URL)), "UTF-8") ;

if (overStr.equals("true")) {

over = true ;

Log.i("db", name+"文件是否下載完成:"+ over );

}else {

over = false ;

Log.i("db",name+ "文件是否下載完成:"+ over );

}

} catch (UnsupportedEncodingException e) {

e.printStackTrace();

}

}

cursor.close();

db.close();

return over;

}

結(jié)束啦~~

總結(jié)

以上是生活随笔為你收集整理的android多线程下载原理,安卓多线程断点续传下载功能(靠谱第三方组件,原理demo)...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 久久久久国产精品夜夜夜夜夜 | 乌克兰少妇性做爰 | 国产网站黄色 | 一区二区三区在线免费视频 | 理论片亚洲 | 欧美大片在线 | 91啪国产| 99re99热| 调教亲女小嫩苞h文小说 | 香蕉久久av| 狠狠一区二区 | 欧洲精品久久一区二区 | 一级黄色a级片 | 黄色图片小说 | 国产精品人成 | 香蕉视频一级 | 91婷婷在线 | 日本xxxx裸体xxxx| 青青青在线 | 国产欧美日韩精品在线 | 国产山村乱淫老妇女视频 | 亚洲精品在线91 | 成人高潮片免费 | 女性向av免费网站 | 黄色大片毛片 | 欧美亚洲国产成人 | 在线天堂www在线国语对白 | 在线视频 亚洲 | 成人免费在线视频网站 | 福利一区在线 | 中文文字幕文字幕高清 | 在线成人看片 | 熟妇一区二区三区 | 在线日韩一区 | 国产aaaaaaa | 捆绑束缚调教 | 影音先锋日韩资源 | 搡国产老太xxx网站 高h喷汁呻吟3p | heyzo朝桐光一区二区 | 婷婷激情网站 | 在线成人日韩 | 国产又大又黄又爽 | 91色吧| 天堂免费av | 狠狠的日| 国产三级国产精品国产国在线观看 | 亚洲国产成人精品久久 | www.日韩高清 | 精品在线小视频 | 在线免费观看av的网站 | 亚洲麻豆av | 一二三区在线观看 | 日本网站在线看 | 日韩av网站在线观看 | 亚洲国产精品网站 | 久久精品国产清自在天天线 | 黄页av | 国产成a人亚洲精v品无码 | 在线观看免费黄色 | 成人7777| 麻豆视频免费在线 | 日本熟妇毛茸茸丰满 | 亚洲电影影音先锋 | 少妇视频| 亚洲蜜桃在线 | 被绑在床强摁做开腿呻吟 | 精品毛片在线观看 | av网站免费在线看 | 久久久久久五月天 | 日韩一区在线观看视频 | 亚洲视屏一区 | 精品国产乱码久久久久久影片 | 91视频国产精品 | 97超碰国产精品无码蜜芽 | 香蕉色视频 | 久色在线 | 国产精品视频网站 | 国产日韩欧美精品 | 2021av在线| 亚洲大胆人体 | 精品国产一区二区三区四区 | 免费精品视频在线观看 | 99久久99久久精品国产片 | 黑人干亚洲 | 在线免费观看视频a | 日本精品视频一区二区 | 91porn破解版 | 在线国产网站 | 国外av网站 | 天天射日 | 中国丰满熟妇xxxx性 | 亚洲精品久久久久久国产精华液 | 亚洲一区二区精品 | 在线视频欧美一区 | 国产精品国产三级国产播12软件 | 免费av观看网站 | 狠操av | 免费一级特黄特色大片 | www.777含羞草 |