国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Android > 正文

Android 斷點續傳原理以及實現

2019-12-12 04:16:54
字體:
來源:轉載
供稿:網友

Android 斷點續傳原理以及實現

0.  前言

在Android開發中,斷點續傳聽起來挺容易,在下載一個文件時點擊暫停任務暫停,點擊開始會繼續下載文件。但是真正實現起來知識點還是蠻多的,因此今天有時間實現了一下,并進行記錄。

1.  斷點續傳原理

在本地下載過程中要使用數據庫實時存儲到底存儲到文件的哪個位置了,這樣點擊開始繼續傳遞時,才能通過HTTP的GET請求中的setRequestProperty()方法可以告訴服務器,數據從哪里開始,到哪里結束。同時在本地的文件寫入時,RandomAccessFile的seek()方法也支持在文件中的任意位置進行寫入操作。同時通過廣播將子線程的進度告訴Activity的ProcessBar。

2.  Activity的按鈕響應

當點擊開始按鈕時,將url寫在了FileInfo類的對象info中并通過Intent從Activity傳遞到了Service中。這里使用setAction()來區分是開始按鈕還是暫停按鈕。

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中取出,如果是開始命令,則開啟一個線程,根據該url去獲得要下載文件的大小,將該大小寫入對象并通過Handler傳回Service,同時在本地創建一個相同大小的本地文件。暫停命令最后會講到。

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;         }         //創建相同大小的本地文件         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.  數據庫操作封裝

在Service的handleMessage()方法中拿到有length屬性的FileInfo對象,并使用自定義的DownLoadUtil類進行具體的文件下載邏輯。這里傳入上下文,因為數據庫處理操作需要用到。

downLoadUtil = new DownLoadUtil(DownLoadService.this,info);downLoadUtil.download();

這里有一個數據庫操作的接口ThreadDAO,內部有增刪改查等邏輯,用于記錄下載任務的信息。自定義一個ThreadDAOImpl類將這里的邏輯實現,內部數據庫創建關于繼承SQLiteOpenHelper的自定義類的邏輯就不貼了,比較簡單,該類會在ThreadDAOImpl類的構造方法中創建實例。完成底層數據庫操作的封裝。

public interface ThreadDAO {   //插入一條數據   public void insert(FileInfo info);   //根據URL刪除一條數據   public void delete(String url);   //根據URL更新一條進度   public void update(String url,int finished);   //根據URL找到一條數據   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){       //第一次下載,創建子線程下載       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() {       //向數據庫添加線程信息       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發廣播         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);             //把進度發送給Activity             finished += len;             //看時間間隔,時間間隔大于500ms再發             if(System.currentTimeMillis() - time >500){               time = System.currentTimeMillis();               intent.putExtra("now",finished *100 /fileInfo.getLength());               context.sendBroadcast(intent);             }             //判斷是否是暫停狀態             if(isPause){               threadDAO.update(info.getUrl(),finished);               return; //結束循環             }           }           //刪除線程信息           threadDAO.delete(info.getUrl());         }       }catch (Exception e){         e.printStackTrace();       }finally {//回收工作略       }     }   } } 

上面也講到使用自定義的DownLoadUtil類進行具體的文件下載邏輯,這也是最關鍵的部分了,在該類的構造方法中進行ThreadDAOImpl實例的創建。并在download()中通過數據庫查詢的操作,判斷是否是第一次開始下載任務,如果是,則開啟一個子線程MyThread進行下載任務,否則將進度信息從數據庫中取出,并將該信息傳遞給MyThread。

在MyThread中,通過info.getStart() + info.getNow()設置開始下載的位置,如果是第一次下載兩個數將都是0,如果是暫停后再下載,則info.getNow()會取出非0值,該值來自數據庫存儲。使用setRequestProperty告知服務器從哪里開始傳遞數據,傳遞到哪里結束,本地使用RandomAccessFile的seek()方法進行數據的本地存儲。使用廣播將進度的百分比傳遞給Activity,Activity再改變ProcessBar進行UI調整。

這里很關鍵的一點是在用戶點擊暫停后會在Service中調用downLoadUtil.isPause = true,因此上面while循環會結束,停止下載并通過數據庫的update()保存進度值。從而在續傳時取出該值,重新對服務器發起文件起始點的下載任務請求,同時也在本地文件的相應位置繼續寫入操作。

6.  效果如下所示


感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 上犹县| 河曲县| 绵竹市| 阿克苏市| 灯塔市| 丹江口市| 扶沟县| 西乡县| 长葛市| 胶州市| 余姚市| 漳州市| 景宁| 丰台区| 元朗区| 开鲁县| 马公市| 仲巴县| 麻阳| 石狮市| 乐昌市| 新建县| 宾川县| 苍梧县| 桐乡市| 仪陇县| 石家庄市| 余干县| 绥德县| 开远市| 莫力| 双桥区| 济源市| 高雄市| 炉霍县| 伽师县| 岢岚县| 屏山县| 神池县| 长白| 鹿泉市|