欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android 斷點(diǎn)續(xù)傳原理以及實(shí)現(xiàn)

 更新時(shí)間:2016年12月20日 10:07:39   投稿:lqh  
這篇文章主要介紹了Android 斷點(diǎn)續(xù)傳原理以及實(shí)現(xiàn)的相關(guān)資料,這里對(duì)斷點(diǎn)續(xù)傳原理進(jìn)行了詳細(xì)介紹,需要的朋友可以參考下

Android 斷點(diǎn)續(xù)傳原理以及實(shí)現(xiàn)

0.  前言

在Android開(kāi)發(fā)中,斷點(diǎn)續(xù)傳聽(tīng)起來(lái)挺容易,在下載一個(gè)文件時(shí)點(diǎn)擊暫停任務(wù)暫停,點(diǎn)擊開(kāi)始會(huì)繼續(xù)下載文件。但是真正實(shí)現(xiàn)起來(lái)知識(shí)點(diǎn)還是蠻多的,因此今天有時(shí)間實(shí)現(xiàn)了一下,并進(jìn)行記錄。

1.  斷點(diǎn)續(xù)傳原理

在本地下載過(guò)程中要使用數(shù)據(jù)庫(kù)實(shí)時(shí)存儲(chǔ)到底存儲(chǔ)到文件的哪個(gè)位置了,這樣點(diǎn)擊開(kāi)始繼續(xù)傳遞時(shí),才能通過(guò)HTTP的GET請(qǐng)求中的setRequestProperty()方法可以告訴服務(wù)器,數(shù)據(jù)從哪里開(kāi)始,到哪里結(jié)束。同時(shí)在本地的文件寫(xiě)入時(shí),RandomAccessFile的seek()方法也支持在文件中的任意位置進(jìn)行寫(xiě)入操作。同時(shí)通過(guò)廣播將子線程的進(jìn)度告訴Activity的ProcessBar。

2.  Activity的按鈕響應(yīng)

當(dāng)點(diǎn)擊開(kāi)始按鈕時(shí),將url寫(xiě)在了FileInfo類(lèi)的對(duì)象info中并通過(guò)Intent從Activity傳遞到了Service中。這里使用setAction()來(lái)區(qū)分是開(kāi)始按鈕還是暫停按鈕。

public class FileInfo implements Serializable{ 
  private String url; //URL 
  private int length; //長(zhǎng)度或結(jié)束位置 
  private int start; //開(kāi)始位置 
  private int now;//當(dāng)前進(jìn)度 
//構(gòu)造方法,set/get略 
} 
//開(kāi)始按鈕邏輯,停止邏輯大致相同 
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對(duì)象從Intent中取出,如果是開(kāi)始命令,則開(kāi)啟一個(gè)線程,根據(jù)該url去獲得要下載文件的大小,將該大小寫(xiě)入對(duì)象并通過(guò)Handler傳回Service,同時(shí)在本地創(chuàng)建一個(gè)相同大小的本地文件。暫停命令最后會(huì)講到。

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) { 
          //獲得文件長(zhǎng)度 
          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); 
        //長(zhǎng)度給fileInfo對(duì)象 
        fileInfo.setLength(length); 
        //通過(guò)Handler將對(duì)象傳遞給Service 
        mHandle.obtainMessage(0, fileInfo).sendToTarget(); 
      } catch (Exception e) { 
        e.printStackTrace(); 
      } finally { //流的回收邏輯略 
      } 
    } 
  } 

4.  數(shù)據(jù)庫(kù)操作封裝

在Service的handleMessage()方法中拿到有l(wèi)ength屬性的FileInfo對(duì)象,并使用自定義的DownLoadUtil類(lèi)進(jìn)行具體的文件下載邏輯。這里傳入上下文,因?yàn)閿?shù)據(jù)庫(kù)處理操作需要用到。

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

downLoadUtil.download();

這里有一個(gè)數(shù)據(jù)庫(kù)操作的接口ThreadDAO,內(nèi)部有增刪改查等邏輯,用于記錄下載任務(wù)的信息。自定義一個(gè)ThreadDAOImpl類(lèi)將這里的邏輯實(shí)現(xiàn),內(nèi)部數(shù)據(jù)庫(kù)創(chuàng)建關(guān)于繼承SQLiteOpenHelper的自定義類(lèi)的邏輯就不貼了,比較簡(jiǎn)單,該類(lèi)會(huì)在ThreadDAOImpl類(lèi)的構(gòu)造方法中創(chuàng)建實(shí)例。完成底層數(shù)據(jù)庫(kù)操作的封裝。

public interface ThreadDAO { 
  //插入一條數(shù)據(jù) 
  public void insert(FileInfo info); 
  //根據(jù)URL刪除一條數(shù)據(jù) 
  public void delete(String url); 
  //根據(jù)URL更新一條進(jìn)度 
  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 { 
  //構(gòu)造方法略 
  public void download(){ 
    List<FileInfo> lists = threadDAO.get(fileInfo.getUrl()); 
    FileInfo info = null; 
    if(lists.size() == 0){ 
      //第一次下載,創(chuàng)建子線程下載 
      new MyThread(fileInfo).start(); 
    }else{ 
      //中間開(kāi)始的 
      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ù)庫(kù)添加線程信息 
      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"); 
        //設(shè)置下載位置 
        int start = info.getStart() + info.getNow(); 
        urlConnection.setRequestProperty("Range","bytes=" + start + "-" + info.getLength()); 
 
        //設(shè)置文件寫(xiě)入位置 
        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){ 
            //寫(xiě)入文件 
            randomFile.write(buffer,0,len); 
            //把進(jìn)度發(fā)送給Activity 
            finished += len; 
            //看時(shí)間間隔,時(shí)間間隔大于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; //結(jié)束循環(huán) 
            } 
          } 
          //刪除線程信息 
          threadDAO.delete(info.getUrl()); 
        } 
      }catch (Exception e){ 
        e.printStackTrace(); 
      }finally {//回收工作略 
      } 
    } 
  } 
} 

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

在MyThread中,通過(guò)info.getStart() + info.getNow()設(shè)置開(kāi)始下載的位置,如果是第一次下載兩個(gè)數(shù)將都是0,如果是暫停后再下載,則info.getNow()會(huì)取出非0值,該值來(lái)自數(shù)據(jù)庫(kù)存儲(chǔ)。使用setRequestProperty告知服務(wù)器從哪里開(kāi)始傳遞數(shù)據(jù),傳遞到哪里結(jié)束,本地使用RandomAccessFile的seek()方法進(jìn)行數(shù)據(jù)的本地存儲(chǔ)。使用廣播將進(jìn)度的百分比傳遞給Activity,Activity再改變ProcessBar進(jìn)行UI調(diào)整。

這里很關(guān)鍵的一點(diǎn)是在用戶點(diǎn)擊暫停后會(huì)在Service中調(diào)用downLoadUtil.isPause = true,因此上面while循環(huán)會(huì)結(jié)束,停止下載并通過(guò)數(shù)據(jù)庫(kù)的update()保存進(jìn)度值。從而在續(xù)傳時(shí)取出該值,重新對(duì)服務(wù)器發(fā)起文件起始點(diǎn)的下載任務(wù)請(qǐng)求,同時(shí)也在本地文件的相應(yīng)位置繼續(xù)寫(xiě)入操作。

6.  效果如下所示


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

相關(guān)文章

最新評(píng)論