Android 斷點續傳功能的實現
目錄

實現效果
按照慣例,效果奉上


Kapture 2018-12-01 at 16.44.09.gif
前言
日更啊!日更!,上一篇比較划水的給大家分享了一下,在做這個功能時候的一點Java基礎,IO流,在這個過程中順便再講一下另一個IO流中的RandomAccessFile類,那麼今天分享一下。
- OKHttp依賴:使用的是OKhttp來連線網路
- RandomAccessFile 來進行IO的讀寫
- BroadcastReceiver:用來接受和更新UI的
正文
OKManger
OKManger
類中實現了 網路連線方法,下載檔案方法,啟用執行緒方法;
public class OkManager { private File rootFile;//檔案的路徑 private File file;//檔案 private long downLoadSize;//下載檔案的長度 private final ThreadPoolExecutor executor;// 執行緒池 private boolean isDown = false; //是否已經下載過了(下載後點擊暫停) 預設為false private String name; //名稱 private String path;// 下載的網址 private RandomAccessFile raf; // 讀取寫入IO方法 private long totalSize = 0; private MyThread thread;//執行緒 private Handler handler;//Handler 方法 private DownLoad.IProgress progress;// 下載進度方法,內部定義的抽象方法 /** * 構造方法OKhttp * @param path網路連線路徑 * @param progress更新路徑 */ public OkManager(String path, DownLoad.IProgress progress) { this.path = path; this.progress = progress; this.handler = new Handler(); this.name = path.substring(path.lastIndexOf("/") + 1); rootFile = FileUtils.getRootFile(); executor = new ThreadPoolExecutor(5, 5, 50, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(3000)); //executor.execute(new MyThread()); } /** *自定義執行緒 */ class MyThread extends Thread { @Override public void run() { super.run(); downLoadFile(); } } /** * 這就是下載方法 */ private void downLoadFile() { try { if (file == null) {//判斷是否擁有相應的檔案 file = new File(rootFile, name); //很正常的File() 方法 raf = new RandomAccessFile(file, "rwd");//例項化一下我們的RandomAccessFile()方法 } else { downLoadSize = file.length();// 檔案的大小 if (raf == null) {//判斷讀取是否為空 raf = new RandomAccessFile(file, "rwd"); } raf.seek(downLoadSize); } totalSize = getContentLength(path);//獲取檔案的大小 if (downLoadSize == totalSize) {// 判斷是否下載完成 //已經下載完成 return; } OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder().url(path). addHeader("Range", "bytes=" + downLoadSize + "-" + totalSize).build(); Response response = client.newCall(request).execute(); InputStream ins = response.body().byteStream(); //上面的就是簡單的OKHttp連線網路,通過輸入流進行寫入到本地 int len = 0; byte[] by = new byte[1024]; long endTime = System.currentTimeMillis(); while ((len = ins.read(by)) != -1 && isDown) {//如果下載沒有出錯並且已經開始下載,迴圈進行以下方法 raf.write(by, 0, len); downLoadSize += len; if (System.currentTimeMillis() - endTime > 1000) { final double dd = downLoadSize / (totalSize * 1.0); DecimalFormat format = new DecimalFormat("#0.00"); String value = format.format((dd * 100)) + "%";//計算百分比 Log.i("tag", "==================" + value); handler.post(new Runnable() {//通過Handler傳送訊息到UI執行緒,更新 @Override public void run() { progress.onProgress((int) (dd * 100)); } }); } } response.close();//最後要把response關閉 } catch (Exception e) { e.getMessage(); } } /** * 執行緒開啟方法 */ public void start() { if (thread == null) { thread = new MyThread(); isDown = true; executor.execute(thread); } } /** * 執行緒停止方法 */ public void stop() { if (thread != null) { isDown = false; executor.remove(thread); thread = null; } } //通過OkhttpClient獲取檔案的大小 public long getContentLength(String url) throws IOException { OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder().url(url).build(); Response response = client.newCall(request).execute(); long length = response.body().contentLength(); response.close(); return length; } public interface IProgress { void onProgress(int progress); } }
以上的方法,大家仔細的看一看,這裡面有詳細的註釋,大家主要關注 downLoadFile()
方法,
FileUtils
public class FileUtils { //判斷是否安裝SDCard public static boolean isSdOk(){ if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){ return true; } return false; } //建立一個資料夾,用來存放下載的檔案 public static File getRootFile(){ File sd = Environment.getExternalStorageDirectory(); File rootFile = new File(sd,"TEMPFILE"); if (!rootFile.exists()){ rootFile.mkdirs(); } return rootFile; } }
這個就是檔案寫入的路徑,封裝的方法,第一個就是判斷是否安裝 SDCard
,第二個方法建立資料夾,存放下載的檔案;
activity_main.xml
佈局檔案,檢視,用了兩個按鈕和progressBar
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="us.mifeng.downloader.MainActivity"> <ProgressBar android:layout_margin="10dp" android:layout_marginTop="20dp" android:id="@+id/progress" android:layout_width="match_parent" android:layout_height="5dp" style="@android:style/Widget.ProgressBar.Horizontal"/> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/progress" android:orientation="horizontal"> <Button android:id="@+id/start" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="開始" /> <Button android:id="@+id/stop" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="停止"/> </LinearLayout> </RelativeLayout>
主方法 Mainactivity()
public class MainActivity extends AppCompatActivity implements View.OnClickListener, DownLoad.IProgress { private String path = "https://download.alicdn.com/wireless/taobao4android/latest/702757.apk"; //private DownLoad downLoad; private ProgressBar pBar; private OkManager downLoad; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //downLoad = new DownLoad(path,this); downLoad = new OkManager(path,this); initView(); } private void initView() { Button start = (Button) findViewById(R.id.start); Button stop = (Button) findViewById(R.id.stop); pBar = (ProgressBar) findViewById(R.id.progress); pBar.setMax(100); start.setOnClickListener(this); stop.setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()){ case R.id.start: downLoad.start(); break; case R.id.stop: downLoad.stop(); break; } } //顯示進度條 @Override public void onProgress(int progress) { pBar.setProgress(progress); } }
很簡單就是呼叫了之前封裝的方法,大家好好地看響應的方法就可以了
總結
- 斷點續傳的關鍵是斷點,所以在制定傳輸協議的時候要設計好,如上圖,我自定義了一個互動協議,每次下載請求都會帶上下載的起始點,這樣就可以支援從斷點下載了, 其實HTTP裡的斷點續傳也是這個原理,在HTTP的頭裡有個可選的欄位RANGE,表示下載的範圍。
- Range : 用於客戶端到伺服器端的請求,可通過該欄位指定下載檔案的某一段大小,及其單位。典型的格式如:
Range: bytes=0-499 下載第0-499位元組範圍的內容
Range: bytes=500-999 下載第500-999位元組範圍的內容
Range: bytes=-500 下載最後500位元組的內容
Range: bytes=500- 下載從第500位元組開始到檔案結束部分的內容
感謝
這個程式碼其實是我在Github上面找到的響應的方法,比較簡單,所以可以更好的放入專案中,希望大家多去大神 ofollow,noindex">github 上面star