Android 史上最優雅的實現檔案上傳、下載及進度的監聽

不怕天黑發表於2019-05-07

本文已授權「劉望舒」微信公眾號獨家原創釋出

前言

本文將直接使用RxHttp庫實現檔案上傳、下載、斷點下載、進度的監聽,不對RxHttp做過多講解,如果對RxHttp不瞭解,請移步

RxHttp 一條鏈傳送請求,新一代Http請求神器(一)

RxHttp 一條鏈傳送請求之強大的資料解析功能(二)

RxHttp 一條鏈傳送請求之強大的Param類(三)

RxHttp 一條鏈傳送請求之註解處理器 Generated API(四)

本文目的在於讓更多的讀者知道RxHttp庫,如果您已閱讀上面4篇文章,本文可直接跳過,感謝你的支援。??。

上傳

  RxHttp.postForm("http://...") //傳送Form表單形式的Post請求
        .add("key", "value")
        .add("file1", new File("xxx/1.png")) //新增file物件
        .add("file2", new File("xxx/2.png"))
        .asString() //asXXX操作符,是非同步操作
        .as(RxLife.asOnMain(this))  //感知生命週期,並在主執行緒回撥
        .subscribe(s -> { 
            //上傳成功,拿到Http返回值,這裡返回值為String型別
        }, throwable -> {
            //上傳失敗
        });
複製程式碼

注:如果需要對Http的返回值做解析,可在使用asParser操作符時,傳入一個解析器Parser

帶進度上傳

帶進度上傳使用asUpload(Progress,Scheduler)操作符

  RxHttp.postForm("http://www.......") //傳送Form表單形式的Post請求
        .add("key1", "value1")//新增引數,非必須
        .add("file1", new File("xxx/1.png"))
        .asUpload(progress -> {
            //上傳進度回撥,0-100,僅在進度有更新時才會回撥,最多回撥101次,最後一次回撥Http執行結果
            int currentProgress = progress.getProgress(); //當前進度 0-100
            long currentSize = progress.getCurrentSize(); //當前已上傳的位元組大小
            long totalSize = progress.getTotalSize();     //要上傳的總位元組大小
        }, AndroidSchedulers.mainThread())//指定主執行緒回撥
        .as(RxLife.as(this))  //感知生命週期
        .subscribe(s -> { //s為String型別,由SimpleParser類裡面的泛型決定的
            //上傳成功,處理相關邏輯
        }, throwable -> {
            //上傳失敗,處理相關邏輯
        });
複製程式碼

注:如果需要對Http的返回值做解析,可使用asUpload(Parser,Progress,Scheduler)方法,傳入一個解析器Parser

下載

  //檔案儲存路徑
  String destPath = getExternalCacheDir() + "/" + System.currentTimeMillis() + ".apk";
  RxHttp.get("http://update.9158.com/miaolive/Miaolive.apk")
        .asDownload(destPath) //注意這裡使用asDownload操作符,並傳入本地路徑
        .as(RxLife.asOnMain(this))  //感知生命週期,並在主執行緒回撥
        .subscribe(s -> {
            //下載成功,回撥檔案下載路徑
        }, throwable -> {
            //下載失敗
        });
複製程式碼

帶進度下載

帶進度下載使用asDownload(String,Consumer,Scheduler)方法

  //檔案儲存路徑
  String destPath = getExternalCacheDir() + "/" + System.currentTimeMillis() + ".apk";
  RxHttp.get("http://update.9158.com/miaolive/Miaolive.apk")
        .asDownload(destPath, progress -> {
            //下載進度回撥,0-100,僅在進度有更新時才會回撥,最多回撥101次,最後一次回撥檔案儲存路徑
            int currentProgress = progress.getProgress(); //當前進度 0-100
            long currentSize = progress.getCurrentSize(); //當前已下載的位元組大小
            long totalSize = progress.getTotalSize();     //要下載的總位元組大小
        }, AndroidSchedulers.mainThread()) //指定主執行緒回撥
        .as(RxLife.as(this)) //感知生命週期
        .subscribe(s -> {//s為String型別,這裡為檔案儲存路徑
            //下載完成,處理相關邏輯
        }, throwable -> {
            //下載失敗,處理相關邏輯
        });
複製程式碼

斷點下載

斷點下載相較於下載,僅需要呼叫setRangeHeader方法傳入開始及結束位置即可(結束位置不傳預設為檔案末尾),其它沒有任何差別

  String destPath = getExternalCacheDir() + "/" + "Miaobo.apk";
  long length = new File(destPath).length(); //已下載的檔案長度
  RxHttp.get("http://update.9158.com/miaolive/Miaolive.apk")
        .setRangeHeader(length)  //設定開始下載位置,結束位置預設為檔案末尾
        .asDownload(destPath)
        .as(RxLife.asOnMain(this)) //加入感知生命週期的觀察者
        .subscribe(s -> { //s為String型別
            Log.e("LJX", "breakpointDownloadAndProgress=" + s);
            //下載成功,處理相關邏輯
        }, throwable -> {
            //下載失敗,處理相關邏輯
        });
複製程式碼

帶進度斷點下載

帶進度斷點下載相較於帶進度下載僅需要呼叫setRangeHeader方法傳入開始及結束位置即可(結束位置不傳預設為檔案末尾),其它沒有任何差別

  String destPath = getExternalCacheDir() + "/" + "Miaobo.apk";
  long length = new File(destPath).length(); //已下載的檔案長度
  RxHttp.get("http://update.9158.com/miaolive/Miaolive.apk")
        .setRangeHeader(length)  //設定開始下載位置,結束位置預設為檔案末尾
        .asDownload(destPath, progress -> {
            //下載進度回撥,0-100,僅在進度有更新時才會回撥
            int currentProgress = progress.getProgress(); //當前進度 0-100
            long currentSize = progress.getCurrentSize(); //當前已下載的位元組大小
            long totalSize = progress.getTotalSize();     //要下載的總位元組大小
        }, AndroidSchedulers.mainThread()) //指定主執行緒回撥
        .as(RxLife.as(this)) //加入感知生命週期的觀察者
        .subscribe(s -> { //s為String型別
            //下載成功,處理相關邏輯
        }, throwable -> {
            //下載失敗,處理相關邏輯
        });
複製程式碼

注:上面帶進度斷點下載中,返回的進度會從0開始,如果需要銜接上次下載的進度,則呼叫asDownload(String,long,Consumer,Scheduler)方法傳入上次已經下載好的長度(第二個引數),如下:

  String destPath = getExternalCacheDir() + "/" + "Miaobo.apk";
  long length = new File(destPath).length(); //已下載的檔案長度
  RxHttp.get("http://update.9158.com/miaolive/Miaolive.apk")
        .setRangeHeader(length)  //設定開始下載位置,結束位置預設為檔案末尾
        .asDownload(destPath, length, progress -> {
            //下載進度回撥,0-100,僅在進度有更新時才會回撥
            int currentProgress = progress.getProgress(); //當前進度 0-100
            long currentSize = progress.getCurrentSize(); //當前已下載的位元組大小
            long totalSize = progress.getTotalSize();     //要下載的總位元組大小
        }, AndroidSchedulers.mainThread()) //指定主執行緒回撥
        .as(RxLife.as(this)) //加入感知生命週期的觀察者
        .subscribe(s -> { //s為String型別
            //下載成功,處理相關邏輯
        }, throwable -> {
            //下載失敗,處理相關邏輯
        });
複製程式碼

多工下載

多工下載我們可以使用RxJava的merge操作符,如下:

List<Observable<String>> downList = new ArrayList<>();
for (int i = 0; i < 3; i++) {
    String destPath = getExternalCacheDir() + "/" + i + ".apk";
    String url = "http://update.9158.com/miaolive/Miaolive.apk"
    Observable<String> down = RxHttp.get(url)
            .asDownload(destPath);
    downList.add(down);
}

//通過RxJava內部執行緒池,多工並行下載
Observable.merge(downList)
        .as(RxLife.as(this))
        .subscribe(s -> {
            //單個任務下載完成
        }, throwable -> {
            //下載出錯
        }, () -> {
            //所有任務下載完成
        });

複製程式碼

如果想監聽每個任務的下載進度,也簡單,用老方法即可,如下:

List<Observable<String>> downList = new ArrayList<>();
for (int i = 0; i < 3; i++) {
    String destPath = getExternalCacheDir() + "/" + i + ".apk";
    String url = "http://update.9158.com/miaolive/Miaolive.apk"
    Observable<String> down = RxHttp.get(url)
            .asDownload(destPath, progress -> {
                //單個下載任務進度回撥
            }, AndroidSchedulers.mainThread())
    downList.add(down);
}

//通過RxJava內部執行緒池,多工並行下載
Observable.merge(downList)
        .as(RxLife.as(this))
        .subscribe(s -> {
            //單個任務下載完成
        }, throwable -> {
            //下載出錯
        }, () -> {
            //所有任務下載完成
        });

複製程式碼

多工上傳

與多工下載同理,不再講述。

小結

好了,檔案上傳、下載相關就介紹到這裡了,到這你會發現,不管是上傳還是下載,進度的監聽都極其的相似,極大的降低了學習成本。怎麼樣?是不是很優雅,歡迎打臉!!

最後,很大一部分功勞都要歸功於RxJava的強大,感謝RxJava,向它致敬!!!!

下一文將繼續使用RxJava強大的操作符,看看它與RxHttp又能擦出怎樣的火花。 轉載請註明出處,謝謝?

相關文章