Android實(shí)現(xiàn)斷點(diǎn)下載的方法
最近做的項(xiàng)目中需要實(shí)現(xiàn)斷點(diǎn)下載,即用戶一次下載可以分多次進(jìn)行,下載過程可以中斷,在目前大多數(shù)的帶離線緩存的軟件都是需要實(shí)現(xiàn)這一功能。本文闡述了通過sqlite3簡單實(shí)現(xiàn)了一個具有斷點(diǎn)下載功能的demo。言歸正傳,開始正文。
設(shè)計(jì)
數(shù)據(jù)庫表存儲元數(shù)據(jù)
DBHelper.java
用于業(yè)務(wù)存儲的Dao
Dao.java
抽象下載信息的Bean
LoadInfo.java
呈現(xiàn)下載信息View
MainActivity.java
存儲下載信息Bean
DownloadInfo.java
封裝好的下載類
Downloader.java
代碼結(jié)構(gòu)
具體實(shí)現(xiàn)
下載信息類:DownloadInfo.java
這里的代碼很簡單,就是一個用來保存下載信息的類,很簡單,沒啥好說的,具體看注釋
package entity; public class DownloadInfo { private int threadId;//線程ID private int startPos;//下載起始位置 private int endPos;//下載結(jié)束位置 private int completeSize;//下載完成量 private String url;//資源URL public DownloadInfo(int tId,int sp, int ep,int cSize,String address){ threadId=tId; startPos=sp; endPos=ep; completeSize = cSize; url=address; } /** * @return the threadId */ public int getThreadId() { return threadId; } /** * @param threadId the threadId to set */ public void setThreadId(int threadId) { this.threadId = threadId; } /** * @return the startPos */ public int getStartPos() { return startPos; } /** * @param startPos the startPos to set */ public void setStartPos(int startPos) { this.startPos = startPos; } /** * @return the endPos */ public int getEndPos() { return endPos; } /** * @param endPos the endPos to set */ public void setEndPos(int endPos) { this.endPos = endPos; } /** * @return the completeSize */ public int getCompleteSize() { return completeSize; } /** * @param completeSize the completeSize to set */ public void setCompleteSize(int completeSize) { this.completeSize = completeSize; } /** * @return the url */ public String getUrl() { return url; } /** * @param url the url to set */ public void setUrl(String url) { this.url = url; } @Override public String toString() { // TODO Auto-generated method stub return "threadId:"+threadId+",startPos:"+startPos+",endPos:"+endPos+",completeSize:"+completeSize+",url:"+url; } }
數(shù)據(jù)庫 DBHelper.java
package db; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; public class DBHelper extends SQLiteOpenHelper { String sql ="create table download_info (id integer PRIMARY KEY AUTOINCREMENT," + "thread_id integer," + "start_pos integer," + "end_pos integer," + "complete_size integer," + "url char)"; public DBHelper(Context context) { // TODO Auto-generated constructor stub super(context, "download.db", null, 1); } @Override public void onCreate(SQLiteDatabase db) { // TODO Auto-generated method stub db.execSQL(sql); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { // TODO Auto-generated method stub } }
數(shù)據(jù)庫業(yè)務(wù)管理類 Dao.java
package db; import java.util.ArrayList; import java.util.List; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import entity.DownloadInfo; public class Dao { private DBHelper dbHelper; public Dao(Context context){ dbHelper = new DBHelper(context); } public boolean isNewTask(String url){ SQLiteDatabase db = dbHelper.getReadableDatabase(); String sql = "select count(*) from download_info where url=?"; Cursor cursor = db.rawQuery(sql, new String[]{url}); cursor.moveToFirst(); int count = cursor.getInt(0); cursor.close(); return count == 0; } public void saveInfo(List<DownloadInfo> infos){ SQLiteDatabase db = dbHelper.getWritableDatabase(); String sql = "insert into download_info(thread_id,start_pos,end_pos,complete_size,url) values(?,?,?,?,?)"; Object[] bindArgs= null; for(DownloadInfo info:infos){ bindArgs=new Object[]{info.getThreadId(),info.getStartPos(),info.getEndPos(),info.getCompleteSize(),info.getUrl()}; db.execSQL(sql, bindArgs); } } public List<DownloadInfo> getInfo(String url){ SQLiteDatabase db = dbHelper.getReadableDatabase(); List<DownloadInfo> infos = new ArrayList<DownloadInfo>(); String sql ="select thread_id,start_pos,end_pos,complete_size,url from download_info where url=?"; Cursor cursor=db.rawQuery(sql, new String[]{url}); while(cursor.moveToNext()){ DownloadInfo info = new DownloadInfo(cursor.getInt(0), cursor.getInt(1), cursor.getInt(2), cursor.getInt(3),cursor.getString(4)); infos.add(info); } cursor.close(); return infos; } public void deleteInfo(String url){ SQLiteDatabase db = dbHelper.getWritableDatabase(); db.delete("download_info", "url=?", new String[]{url}); db.close(); } public void updateInfo(int completeSize,int threadId,String url){ SQLiteDatabase db = dbHelper.getWritableDatabase(); String sql ="update download_info set complete_size=? where thread_id=? and url=?"; db.execSQL(sql, new Object[]{completeSize,threadId,url}); } public void closeDB(){ dbHelper.close(); } }
當(dāng)前狀態(tài)保存類 LoadInfo.java
package entity; public class LoadInfo { private int fileSize; private int completeSize; private String url; public LoadInfo(int fs,int cSize,String address){ fileSize=fs; completeSize = cSize; url=address; } /** * @return the fileSize */ public int getFileSize() { return fileSize; } /** * @param fileSize the fileSize to set */ public void setFileSize(int fileSize) { this.fileSize = fileSize; } /** * @return the completeSize */ public int getCompleteSize() { return completeSize; } /** * @param completeSize the completeSize to set */ public void setCompleteSize(int completeSize) { this.completeSize = completeSize; } /** * @return the url */ public String getUrl() { return url; } /** * @param url the url to set */ public void setUrl(String url) { this.url = url; } }
下載助手類:Downloader.java
package com.winton.downloadmanager; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.RandomAccessFile; import java.net.HttpURLConnection; import java.net.MalformedURLException; 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 db.Dao; import entity.DownloadInfo; import entity.LoadInfo; public class Downloader { private String url; private String localPath; private int threadCount; private Handler mHanler; private Dao dao; private int fileSize; private List<DownloadInfo> infos; private final static int INIT = 1; private final static int DOWNLOADING =2; private final static int PAUSE =3; private int state = INIT; public Downloader(String address,String lPath,int thCount,Context context,Handler handler){ url =address; localPath = lPath; threadCount = thCount; mHanler = handler; dao = new Dao(context); } public boolean isDownloading(){ return state == DOWNLOADING; } public LoadInfo getDownLoadInfos(){ if(isFirstDownload(url)){ init(); int range = fileSize/threadCount; infos = new ArrayList<DownloadInfo>(); for(int i=0;i<=threadCount-1;i++){ DownloadInfo info = new DownloadInfo(i, i*range, (i+1)*range-1, 0, url); infos.add(info); } dao.saveInfo(infos); return new LoadInfo(fileSize, 0, url); }else{ infos = dao.getInfo(url); int size = 0; int completeSize =0; for(DownloadInfo info:infos){ completeSize += info.getCompleteSize(); size += info.getEndPos()-info.getStartPos()+1; } return new LoadInfo(size, completeSize, url); } } public boolean isFirstDownload(String url){ return dao.isNewTask(url); } public void init(){ try { URL mUrl = new URL(this.url); HttpURLConnection connection = (HttpURLConnection)mUrl.openConnection(); connection.setConnectTimeout(5000); connection.setRequestMethod("GET"); fileSize = connection.getContentLength(); File file = new File(localPath); if(!file.exists()){ file.createNewFile(); } RandomAccessFile accessFile = new RandomAccessFile(file, "rwd"); accessFile.setLength(fileSize); accessFile.close(); connection.disconnect(); } catch (MalformedURLException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public void download(){ if(infos != null){ if(state ==DOWNLOADING){ return; } state = DOWNLOADING; for(DownloadInfo info:infos){ new DownloadThread(info.getThreadId(), info.getStartPos(), info.getEndPos(), info.getCompleteSize(), info.getUrl()).start(); } } } class DownloadThread extends Thread{ private int threadId; private int startPos; private int endPos; private int completeSize; private String url; public DownloadThread(int tId,int sp,int ep,int cSize,String address) { // TODO Auto-generated constructor stub threadId=tId; startPos=sp; endPos = ep; completeSize = cSize; url = address; } @Override public void run() { // TODO Auto-generated method stub HttpURLConnection connection = null; RandomAccessFile randomAccessFile = null; InputStream is = null; try { URL mUrl = new URL(url); connection = (HttpURLConnection)mUrl.openConnection(); connection.setConnectTimeout(5000); connection.setRequestMethod("GET"); connection.setRequestProperty("Range", "bytes="+(startPos+completeSize)+"-"+endPos); randomAccessFile = new RandomAccessFile(localPath, "rwd"); randomAccessFile.seek(startPos+completeSize); is=connection.getInputStream(); byte[] buffer = new byte[4096]; int length =-1; while((length=is.read(buffer)) != -1){ randomAccessFile.write(buffer, 0, length); completeSize +=length; dao.updateInfo(threadId, completeSize, url); Message msg = Message.obtain(); msg.what=1; msg.obj=url; msg.arg1=length; mHanler.sendMessage(msg); if(state==PAUSE){ return; } } } catch (MalformedURLException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally{ try { is.close(); randomAccessFile.close(); connection.disconnect(); dao.closeDB(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } public void delete(String url){ dao.deleteInfo(url); } public void reset(){ state=INIT; } public void pause(){ state=PAUSE; } }
View呈現(xiàn)類:MainActivity.java
package com.winton.downloadmanager; import android.app.Activity; import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.os.Message; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; import entity.LoadInfo; public class MainActivity extends Activity implements OnClickListener{ private TextView name; private ProgressBar process; private Button start,stop; private Downloader downloader; //處理下載進(jìn)度UI的 Handler handler = new Handler(){ public void handleMessage(android.os.Message msg) { if(msg.what==1){ name.setText((String)msg.obj); int lenght = msg.arg1; process.incrementProgressBy(lenght); } if(msg.what==2){ int max =msg.arg1; process.setMax(max); Toast.makeText(getApplicationContext(), max+"", 1).show(); } }; }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); name=(TextView)findViewById(R.id.tv_name); process=(ProgressBar)findViewById(R.id.pb_download); start = (Button)findViewById(R.id.bt_start); stop = (Button)findViewById(R.id.bt_stop); start.setOnClickListener(this); stop.setOnClickListener(this); downloader=new Downloader("http://img4.duitang.com/uploads/item/201206/11/20120611174542_5KRMj.jpeg", Environment.getExternalStorageDirectory().getPath()+"/test1.jpg", 1, getApplicationContext(), handler); } @Override public void onClick(View v) { // TODO Auto-generated method stub if(v==start){ new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub LoadInfo loadInfo = downloader.getDownLoadInfos(); Message msg =handler.obtainMessage(); msg.what=2; msg.arg1=loadInfo.getFileSize(); handler.sendMessage(msg); downloader.download(); } }).start(); return; } if(v==stop){ downloader.pause(); return; } } }
運(yùn)行效果
總體比較簡單,基本實(shí)現(xiàn)了 斷點(diǎn)下載的功能,而且開啟了多個線程去實(shí)現(xiàn)分塊下載,對初學(xué)的同學(xué)有一定的幫助。
這些代碼我也是從網(wǎng)上各種搜集而來,自己親自動手敲了一遍,并做了一些小的改動,對整個斷點(diǎn)下載的過程有了一個深刻的認(rèn)識。因此平時要多敲代碼,善于總結(jié),必能有所收獲。
- Android 斷點(diǎn)下載和自動安裝的示例代碼
- android多線程斷點(diǎn)下載-帶進(jìn)度條和百分比進(jìn)度顯示效果
- Android HttpURLConnection斷點(diǎn)下載(單線程)
- Android原生實(shí)現(xiàn)多線程斷點(diǎn)下載實(shí)例代碼
- 詳解Android中的多線程斷點(diǎn)下載
- Android入門:多線程斷點(diǎn)下載詳細(xì)介紹
- Android使用多線程實(shí)現(xiàn)斷點(diǎn)下載
- Android實(shí)現(xiàn)多線程斷點(diǎn)下載的方法
- Android實(shí)現(xiàn)斷點(diǎn)多線程下載
相關(guān)文章
Androd自定義對話框Dialog視圖及參數(shù)傳遞的實(shí)現(xiàn)方法
這篇文章主要介紹了Androd自定義對話框Dialog視圖及參數(shù)傳遞的實(shí)現(xiàn)方法,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2017-01-01MUI進(jìn)行APP混合開發(fā)實(shí)現(xiàn)下拉刷新和上拉加載
給大家分析一下在用MUI進(jìn)行APP混合開發(fā)的時候,如何用代碼實(shí)現(xiàn)下拉刷新和上拉加載這個普遍應(yīng)用的功能。2017-11-11解析Android開發(fā)優(yōu)化之:對界面UI的優(yōu)化詳解(一)
在Android應(yīng)用開發(fā)過程中,屏幕上控件的布局代碼和程序的邏輯代碼通常是分開的。界面的布局代碼是放在一個獨(dú)立的xml文件中的,這個文件里面是樹型組織的,控制著頁面的布局2013-05-055個Android開發(fā)中比較常見的內(nèi)存泄漏問題及解決辦法
本文主要介紹了5個Android開發(fā)中比較常見的內(nèi)存泄漏問題及解決辦法,具有很好的參考價值,下面跟著小編一起來看下吧2017-02-02Android使用AndroidUtilCode實(shí)現(xiàn)多語言
這篇文章主要為大家介紹了Android使用AndroidUtilCode實(shí)現(xiàn)多語言示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01Android RatingBar星星評分控件實(shí)例代碼
本文通過實(shí)例代碼給大家介紹了Android RatingBar星星評分控件,非常不錯,具有參考借鑒價值,需要的朋友參考下吧2017-06-06Android編程設(shè)計(jì)模式之抽象工廠模式詳解
這篇文章主要介紹了Android編程設(shè)計(jì)模式之抽象工廠模式,結(jié)合實(shí)例形式詳細(xì)分析了Android抽象工廠模式的概念、原理、使用方法及相關(guān)注意事項(xiàng),需要的朋友可以參考下2017-12-12Android中區(qū)別Drawable Bitmap Canvas Paint
本文主要介紹Android中Drawable Bitmap Canvas Paint 之間的區(qū)別,這里對這幾個概念做出詳細(xì)介紹,開發(fā)Android游戲的朋友可以參考下2016-07-07Android 軟鍵盤出現(xiàn)不適應(yīng)的解決辦法總結(jié)
這篇文章主要介紹了Android 軟鍵盤出現(xiàn)不適應(yīng)的解決辦法總結(jié)的相關(guān)資料,需要的朋友可以參考下2017-03-03