天天看點

Retrofit 2.0 超能實踐(四),完成大檔案斷點下載下傳ApiServiceDownLoadManager

作者:碼小白

文/CSDN 部落格

本文出自:http://blog.csdn.net/sk719887916/article/details/51988507 碼小白

通過前幾篇系統的介紹和綜合運用,忘記介紹檔案下載下傳功能了,有朋友問到,目前APP檔案下載下傳主要有斷點續傳,多線程并發下載下傳,多類型下載下傳,今天就介紹下其Retrofit下載下傳檔案功能。

  • Retrofit 2.0

    超能實踐,完美支援Https傳輸

  • Retrofit2.0

    完美同步Cookie實作免登入

  • Retrofit 2.0 超能實踐(三),輕松實作檔案/圖檔上傳
  • 基于Retrofit2.0 封裝的超好用的RetrofitClient工具類
  • 玩轉IOC,教你徒手實作自定義的Retrofit架構

ApiService

編寫API,執行下載下傳接口功能。

public interface ApiService {
  @Streaming
  @GET
  Observable<ResponseBody> downloadFile(@Url String   fileUrl);
}
           

url由于是可變的,是以用

@URL

注解符号來進行指定,大檔案官方建議用

@Streaming

來進行注解,不然會出現IO異常,小檔案可以忽略不注入。如果想進行斷點續傳的話 可以在此加入header,但不建議直接在api中寫死,每個下載下傳的請求大小是不同的,在攔截器加入更為妥善。

DownLoadManager

實作一個下載下傳管理者 來進行檔案寫入,類型判斷等,如果想做完善,可以判斷下http的請求頭

content-length

,

content- type

,

RANGE

第一個用來判斷檔案大小,第二個檔案類型,第三個是檔案從哪兒開始下載下傳,如果對下載下傳來源校驗可以加入

referer

, 不是目标來源的可以不予下載下傳權限。

public class DownLoadManager {

  private static final String TAG = "DownLoadManager";

 private static String APK_CONTENTTYPE = "application/vnd.android.package-archive";

 private static String PNG_CONTENTTYPE = "image/png";

 private static String JPG_CONTENTTYPE = "image/jpg";

 private static String fileSuffix="";

 public static boolean  writeResponseBodyToDisk(Context context, ResponseBody body) {

    Log.d(TAG, "contentType:>>>>"+ body.contentType().toString());

    String type = body.contentType().toString();

    if (type.equals(APK_CONTENTTYPE)) {

       fileSuffix = ".apk";
    } else if (type.equals(PNG_CONTENTTYPE)) {
        fileSuffix = ".png";
    }

    // 其他同上 自己判斷加入


    String path = context.getExternalFilesDir(null) + File.separator + System.currentTimeMillis() + fileSuffix;

    Log.d(TAG, "path:>>>>"+ path);

    try {
        // todo change the file location/name according to your needs
        File futureStudioIconFile = new File(path);

        InputStream inputStream = null;
        OutputStream outputStream = null;

        try {
            byte[] fileReader = new byte[4096];

            long fileSize = body.contentLength();
            long fileSizeDownloaded = 0;

            inputStream = body.byteStream();
            outputStream = new FileOutputStream(futureStudioIconFile);

            while (true) {
                int read = inputStream.read(fileReader);

                if (read == -1) {
                    break;
                }

                outputStream.write(fileReader, 0, read);

                fileSizeDownloaded += read;

                Log.d(TAG, "file download: " + fileSizeDownloaded + " of " + fileSize);
            }

            outputStream.flush();


            return true;
        } catch (IOException e) {
            return false;
        } finally {
            if (inputStream != null) {
                inputStream.close();
            }

            if (outputStream != null) {
                outputStream.close();
            }
        }
    } catch (IOException e) {
        return false;
    }
}
           

}

上面隻是簡單的對不同類型檔案進行了判斷,其他類型參考

http

協定描述類型自行加入,斷點續傳目前暫時沒實作,基于HttpClinet下載下傳的介紹很多,關鍵思想是記錄一條下載下傳産生後的

RANGE

到資料庫裡,每條下載下傳行對應一條資料,下載下傳ID是區分唯一的主鍵, 包含

filename

,

fileSize, fileType, downTime, downRange

status

基本可滿足多線程下載下傳資料要求,在Retrofit結合RXJava後 解決了線程安全問題,實作多線程下載下傳就更so yi z, 這裡我也不做多的介紹。

#調用下載下傳

OkHttpClient okHttpClient = new OkHttpClient.Builder()
            .build();
 Retrofit retrofit = new Retrofit.Builder()
            .client(okHttpClient)
            .baseUrl(url)
            .build();
Api Service apiService = retrofit.create(ApiService.class);

apiService..download(url1,
                    new Subscriber<ResponseBody>() {
                        @Override
                        public void onCompleted() {

                        }

                        @Override
                        public void onError(Throwable e) {

                        }

                        @Override
                        public void onNext(ResponseBody responseBody) {

                            if (DownLoadManager.writeResponseBodyToDisk(MainActivity.this, responseBody)) {
                                Toast.makeText(MainActivity.this, "Download is sucess", Toast.LENGTH_LONG).show();
                            }

                        }
                    });
        }
    });
           

當 然我們可以在header中加入上次下載下傳的進度大小,就可以實作基本的斷點下載下傳了,多線程下載下傳目前業内主要由下面思想實作,我們可以請求到檔案總長度的時候,均分三段大小區間,來開啟三個新線程去下載下傳,下載下傳完後再以唯一的檔案ID,将三段檔案以此追加到一個檔案就可以了,當然在retrofit結合RxJava,你無需開啟新線程,隻需訂閱一下 Subscriber到IO線程即可,這裡注意的是你需要判斷最後的檔案大小和HashCode來進行驗證檔案完整性,不然視訊檔案丢包了沒多大差別,最多會卡幀,但是apk丢包了卻無法解析安裝了。

斷點下載下傳姿勢開啟!

#斷點下載下傳

##apiServiece

@GET
@Streaming
Observable<Response<ResponseBody>> download(@Header("Range") String range, @Url String url);
           

##API

DownLoadInfos裡面包含name,lenth,.url,等,這裡不再貼了。直接看API

DownloadType 裡面記錄要有stats,mLastModify,downloded ,savepath等

public Observable<DownloadInfos> download(@NonNull final String url, @NonNull final String saveName,
   @Nullable final String savePath) {
return downloadDispatcher(url, saveName, savePath);
}





private Observable<DownloadStatus> downloadDispatcher(final String url, final String saveName, final String savePath) {
    return getDownloadType(url)
            .flatMap(new Func1<DownloadType, Observable<DownloadStatus>>() {
                @Override
                public Observable<DownloadInfos> call(DownloadType type) {
                    try {
                        type.prepare();
                    } catch (IOException | ParseException e) {
                        return Observable.error(e);
                    }
                    try {
                        return type.start();
                    } catch (IOException e) {
                        return Observable.error(e);
                    }
                }
            })
            .doOnCompleted(new Action0() {
                @Override
                public void call() {
                    mDownloadManager.delete(url);
                }
            })
            .doOnError(new Action1<Throwable>() {
                @Override
                public void call(Throwable throwable) {
                   
                    mDownloadManager.delete(url);
                }
            })
            .doOnUnsubscribe(new Action0() {
                @Override
                public void call() {
                    mDownloadManager.delete(url)
            });
}
           

開始下載下傳:

@Override
    Observable<DownloadInfos> start() {
        Log.i(TAG, "Normal download start!!");
        return mDownloadHelper.getDownloadApi().download(null, mUrl)
                .subscribeOn(Schedulers.io())
                .flatMap(new Func1<Response<ResponseBody>, Observable<DownloadStatus>>() {
                    @Override
                    public Observable<DownloadStatus> call(final Response<ResponseBody> response) {
                        return normalSave(response);
                    }
                }).onBackpressureLatest().retry(new Func2<Integer, Throwable, Boolean>() {
                    @Override
                    public Boolean call(Integer integer, Throwable throwable) {
                        return mDownloadHelper.retry(integer, throwable);
                    }
                });
    }
           

##調用

DownloadAgenti.getInstance(mContext)
                .observeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Subscriber<DownloadInfos>() {
                    @Override
                    public void onCompleted() {
                        // todo
                    }

                    @Override
                    public void onError(Throwable e) {
                       todo
                    }

                    @Override
                    public void onNext(final DownloadStatus status) {
                       todo
                    }
                }); 
           

#總結

步驟:

請求檔案總大小

根據機型高低,配置設定多個線程下載下傳

記錄下載下傳進度,大小,類型等到資料庫

同時更新UI和通知欄,提示使用者

下載下傳結束後更新資料庫下載下傳資料,追加組合檔案

判斷檔案大小,檢驗檔案大小

下載下傳功能是每個應用必須的功能,是以下載下傳還是比較重要的一個子產品,好的下載下傳架構 會考慮到裝置的CPU大小,網絡狀态,以及外部sdcard的大小,動态進行性能,流量配置設定,友善使用者更好的體驗和使用你的APP.

具體可以閱讀 - 基于Retrofit2.0 封裝的超好用的RetrofitClient工具類 中的寫法

Retrofit 2.0 超能實踐(四),完成大檔案斷點下載下傳ApiServiceDownLoadManager

項目github:https://github.com/Tamicer/FastDownloader

作者:Tamic 更多原創關注開發者技術前線
Retrofit 2.0 超能實踐(四),完成大檔案斷點下載下傳ApiServiceDownLoadManager

繼續閱讀