Android 斷點續(xù)傳原理以及實現(xiàn)
Android 斷點續(xù)傳原理以及實現(xiàn)
0. 前言
在Android開發(fā)中,斷點續(xù)傳聽起來挺容易,在下載一個文件時點擊暫停任務暫停,點擊開始會繼續(xù)下載文件。但是真正實現(xiàn)起來知識點還是蠻多的,因此今天有時間實現(xiàn)了一下,并進行記錄。
1. 斷點續(xù)傳原理
在本地下載過程中要使用數(shù)據(jù)庫實時存儲到底存儲到文件的哪個位置了,這樣點擊開始繼續(xù)傳遞時,才能通過HTTP的GET請求中的setRequestProperty()方法可以告訴服務器,數(shù)據(jù)從哪里開始,到哪里結束。同時在本地的文件寫入時,RandomAccessFile的seek()方法也支持在文件中的任意位置進行寫入操作。同時通過廣播將子線程的進度告訴Activity的ProcessBar。
2. Activity的按鈕響應
當點擊開始按鈕時,將url寫在了FileInfo類的對象info中并通過Intent從Activity傳遞到了Service中。這里使用setAction()來區(qū)分是開始按鈕還是暫停按鈕。
public class FileInfo implements Serializable{ private String url; //URL private int length; //長度或結束位置 private int start; //開始位置 private int now;//當前進度 //構造方法,set/get略 } //開始按鈕邏輯,停止邏輯大致相同 strat.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent intent = new Intent(MainActivity.this,DownLoadService.class); intent.setAction(DownLoadService.ACTION_START); intent.putExtra("fileUrl",info); startService(intent); } });
3. 在Service中的子線程中獲取文件大小
在Service中的onStartCommand()中,將FileInfo對象從Intent中取出,如果是開始命令,則開啟一個線程,根據(jù)該url去獲得要下載文件的大小,將該大小寫入對象并通過Handler傳回Service,同時在本地創(chuàng)建一個相同大小的本地文件。暫停命令最后會講到。
public void run() { HttpURLConnection urlConnection = null; RandomAccessFile randomFile = null; try { URL url = new URL(fileInfo.getUrl()); urlConnection = (HttpURLConnection) url.openConnection(); urlConnection.setConnectTimeout(3000); urlConnection.setRequestMethod("GET"); int length = -1; if (urlConnection.getResponseCode() == HttpStatus.SC_OK) { //獲得文件長度 length = urlConnection.getContentLength(); } if (length <= 0) { return; } //創(chuàng)建相同大小的本地文件 File dir = new File(DOWNLOAD_PATH); if (!dir.exists()) { dir.mkdir(); } File file = new File(dir, FILE_NAME); randomFile = new RandomAccessFile(file, "rwd"); randomFile.setLength(length); //長度給fileInfo對象 fileInfo.setLength(length); //通過Handler將對象傳遞給Service mHandle.obtainMessage(0, fileInfo).sendToTarget(); } catch (Exception e) { e.printStackTrace(); } finally { //流的回收邏輯略 } } }
4. 數(shù)據(jù)庫操作封裝
在Service的handleMessage()方法中拿到有l(wèi)ength屬性的FileInfo對象,并使用自定義的DownLoadUtil類進行具體的文件下載邏輯。這里傳入上下文,因為數(shù)據(jù)庫處理操作需要用到。
downLoadUtil = new DownLoadUtil(DownLoadService.this,info); downLoadUtil.download();
這里有一個數(shù)據(jù)庫操作的接口ThreadDAO,內(nèi)部有增刪改查等邏輯,用于記錄下載任務的信息。自定義一個ThreadDAOImpl類將這里的邏輯實現(xiàn),內(nèi)部數(shù)據(jù)庫創(chuàng)建關于繼承SQLiteOpenHelper的自定義類的邏輯就不貼了,比較簡單,該類會在ThreadDAOImpl類的構造方法中創(chuàng)建實例。完成底層數(shù)據(jù)庫操作的封裝。
public interface ThreadDAO { //插入一條數(shù)據(jù) public void insert(FileInfo info); //根據(jù)URL刪除一條數(shù)據(jù) public void delete(String url); //根據(jù)URL更新一條進度 public void update(String url,int finished); //根據(jù)URL找到一條數(shù)據(jù) public List<FileInfo> get(String url); //是否存在 public boolean isExits(String url); }
5. 具體的文件下載邏輯
public class DownLoadUtil { //構造方法略 public void download(){ List<FileInfo> lists = threadDAO.get(fileInfo.getUrl()); FileInfo info = null; if(lists.size() == 0){ //第一次下載,創(chuàng)建子線程下載 new MyThread(fileInfo).start(); }else{ //中間開始的 info = lists.get(0); new MyThread(info).start(); } } class MyThread extends Thread{ private FileInfo info = null; public MyThread(FileInfo threadInfo) { this.info = threadInfo; } @Override public void run() { //向數(shù)據(jù)庫添加線程信息 if(!threadDAO.isExits(info.getUrl())){ threadDAO.insert(info); } HttpURLConnection urlConnection = null; RandomAccessFile randomFile =null; InputStream inputStream = null; try { URL url = new URL(info.getUrl()); urlConnection = (HttpURLConnection) url.openConnection(); urlConnection.setConnectTimeout(3000); urlConnection.setRequestMethod("GET"); //設置下載位置 int start = info.getStart() + info.getNow(); urlConnection.setRequestProperty("Range","bytes=" + start + "-" + info.getLength()); //設置文件寫入位置 File file = new File(DOWNLOAD_PATH,FILE_NAME); randomFile = new RandomAccessFile(file, "rwd"); randomFile.seek(start); //向Activity發(fā)廣播 Intent intent = new Intent(ACTION_UPDATE); finished += info.getNow(); if (urlConnection.getResponseCode() == HttpStatus.SC_PARTIAL_CONTENT) { //獲得文件流 inputStream = urlConnection.getInputStream(); byte[] buffer = new byte[512]; int len = -1; long time = System.currentTimeMillis(); while ((len = inputStream.read(buffer))!= -1){ //寫入文件 randomFile.write(buffer,0,len); //把進度發(fā)送給Activity finished += len; //看時間間隔,時間間隔大于500ms再發(fā) if(System.currentTimeMillis() - time >500){ time = System.currentTimeMillis(); intent.putExtra("now",finished *100 /fileInfo.getLength()); context.sendBroadcast(intent); } //判斷是否是暫停狀態(tài) if(isPause){ threadDAO.update(info.getUrl(),finished); return; //結束循環(huán) } } //刪除線程信息 threadDAO.delete(info.getUrl()); } }catch (Exception e){ e.printStackTrace(); }finally {//回收工作略 } } } }
上面也講到使用自定義的DownLoadUtil類進行具體的文件下載邏輯,這也是最關鍵的部分了,在該類的構造方法中進行ThreadDAOImpl實例的創(chuàng)建。并在download()中通過數(shù)據(jù)庫查詢的操作,判斷是否是第一次開始下載任務,如果是,則開啟一個子線程MyThread進行下載任務,否則將進度信息從數(shù)據(jù)庫中取出,并將該信息傳遞給MyThread。
在MyThread中,通過info.getStart() + info.getNow()設置開始下載的位置,如果是第一次下載兩個數(shù)將都是0,如果是暫停后再下載,則info.getNow()會取出非0值,該值來自數(shù)據(jù)庫存儲。使用setRequestProperty告知服務器從哪里開始傳遞數(shù)據(jù),傳遞到哪里結束,本地使用RandomAccessFile的seek()方法進行數(shù)據(jù)的本地存儲。使用廣播將進度的百分比傳遞給Activity,Activity再改變ProcessBar進行UI調整。
這里很關鍵的一點是在用戶點擊暫停后會在Service中調用downLoadUtil.isPause = true,因此上面while循環(huán)會結束,停止下載并通過數(shù)據(jù)庫的update()保存進度值。從而在續(xù)傳時取出該值,重新對服務器發(fā)起文件起始點的下載任務請求,同時也在本地文件的相應位置繼續(xù)寫入操作。
6. 效果如下所示
感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!
- 詳解Android使用OKHttp3實現(xiàn)下載(斷點續(xù)傳、顯示進度)
- android實現(xiàn)多線程下載文件(支持暫停、取消、斷點續(xù)傳)
- android使用OkHttp實現(xiàn)下載的進度監(jiān)聽和斷點續(xù)傳
- Android FTP 多線程斷點續(xù)傳下載\上傳的實例
- Android多線程斷點續(xù)傳下載功能實現(xiàn)代碼
- Android多線程+單線程+斷點續(xù)傳+進度條顯示下載功能
- Android 斷點續(xù)傳的原理剖析與實例講解
- Android實現(xiàn)網(wǎng)絡多線程斷點續(xù)傳下載實例
- Android編程開發(fā)實現(xiàn)多線程斷點續(xù)傳下載器實例
- Android快速實現(xiàn)斷點續(xù)傳的方法
相關文章
設置Android設備WIFI在休眠時永不斷開的代碼實現(xiàn)
這篇文章主要介紹了設置Android設備WIFI在休眠時永不斷開的代碼實現(xiàn),需要的朋友可以參考下2014-07-07Flutter實現(xiàn)頁面切換后保持原頁面狀態(tài)的3種方法
這篇文章主要給大家介紹了關于Flutter實現(xiàn)頁面切換后保持原頁面狀態(tài)的3種方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者使用Flutter具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧2019-03-03android 使用okhttp可能引發(fā)OOM的一個點
這篇文章主要介紹了android 使用okhttp可能引發(fā)OOM的一個點,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-10-10Android通過ksoap2傳遞復雜數(shù)據(jù)類型及CXF發(fā)布的webservice詳細介紹
這篇文章主要介紹了 Android通過ksoap2傳遞復雜數(shù)據(jù)類型詳細介紹的相關資料,需要的朋友可以參考下2017-02-02