SimpleDownload 一款Android下載下傳架構的講解
-
-
- 一、本架構實作的功能特性
- 二、最終效果圖,及使用方法
- 三、關鍵功能源碼說明
-
- 1.檔案下載下傳和普通網絡請求的不同
- 2. 斷點續傳如何實作
- 3. 如何得到檔案下載下傳進度
- 4. 如何把回調切換到主線程
-
本文介紹自己封裝的一套下載下傳架構。
首先要知道,檔案下載下傳和我們平時調接口一樣,要發起網絡請求的,是以我們先找個合适的網絡請求庫,這裡我們選擇Retrofit+OkHttp的組合,為了友善支援回調線程切換等功能再加上RxJava的組合。
本架構已更新多次,而本文章可能未及時更新。詳細介紹說明請看github
https://github.com/liwuchen/SimpleDownload
一、本架構實作的功能特性
本下載下傳架構實作了這樣的特性:
1. 支援斷點續傳。即下載下傳斷開後,支援從已完成的部分開始繼續下載下傳。
2. 支援多個任務同時下載下傳。
3. 有各種情況的回調。如下載下傳進度回調,任務取消、完成等回調。
4. 下載下傳任務在子線程進行,回調在主線程。不需要使用者自己切換線程。
5. 使用者可以自定義檔案儲存路徑和檔案名。
二、最終效果圖,及使用方法

本架構源碼及Demo已上傳github,位址:https://github.com/liwuchen/SimpleDownload
如果想在Android Studio項目中使用本架構,可以按如下方式內建:
1.項目根目錄下的Build.gradle檔案中修改:
allprojects {
repositories {
google()
jcenter()
maven { url 'https://www.jitpack.io' } // 添加這行
}
}
2.子產品Build.gradle檔案添加:
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
api 'com.github.liwuchen:SimpleDownload:1.4.0' // 添加這行
}
請使用最新版本。
3.添權重限(讀寫檔案的權限還需動态申請,這裡就不貼出了):
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
4.代碼中使用起來也很簡單:
// 自定義檔案夾名
private final String FOLDER_NAME = "SimpleDownload";
// 檔案儲存路徑
private final String SAVE_PATH = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + FOLDER_NAME;
// 檔案下載下傳位址
private final String url = "http://ccr.csslcloud.net/5D2636511DBBCADD/BBD5D1D6504FF2AD9C33DC5901307461/8DE588DAEE2FE914.ccr";
// 儲存檔案名
private final String fileName = "1.file";
// 定義下載下傳回調
DownloadListener listener = new DownloadListener() {
@Override
public void onStartDownload() {}
@Override
public void onProgress(long downloaded, long total) {}
@Override
public void onPauseDownload() {}
@Override
public void onCancelDownload() {}
@Override
public void onFinishDownload(String savedFile) {}
@Override
public void onFail(String errorInfo) {}
};
DownloadManager downloadManager = DownloadManager.getInstance();
// 開始下載下傳
downloadManager.download(url, SAVE_PATH, fileName, listener);
// 暫停下載下傳
downloadManager.pauseDownload(url);
// 繼續下載下傳
downloadManager.continueDownload(url);
// 取消下載下傳,不删除檔案
downloadManager.cancelDownload(url, false);
// 取消下載下傳,并删除檔案
downloadManager.cancelDownload(url, true);
三、關鍵功能源碼說明
本架構的源碼類如下:
1.檔案下載下傳和普通網絡請求的不同
熟悉Retrofit的同學肯定知道,使用Retrofit發起網絡請求,需要編寫這樣的接口:
@Streaming
@GET
Observable<ResponseBody> rxDownload(@Header("RANGE") String start, @Url String url);
而這裡我們加上了@Streaming注解,這表明,請求得到的資料會以流的形式傳回,即檔案下載下傳。
2. 斷點續傳如何實作
首先知道,在網絡請求時,如果Header加上Range: bytes=xx,則表示從xx個位元組開始下載下傳。
那麼我們隻要繼續下載下傳檔案時,header裡指定開始的位置即可。
本架構裡,我們上面貼出的接口裡,已含有"RANGE"這個header,使用的時候傳入參數即可。
3. 如何得到檔案下載下傳進度
本架構使用了自定義攔截器+自定義ResponseBody的方式。
使用攔截器攔截傳回的資料,然後将資料轉換成自定義ResponseBody。就可以得到下載下傳進度了。
這裡要提一下,有的伺服器傳回資料時,未傳回檔案的總大小,是以進度回調裡傳回的總大小是-1,在時用的時候要判斷下。
4. 如何把回調切換到主線程
通過RxJava實作,很友善。
如果對RxJava不熟悉的話,可以看我的另一篇關于RxJava的入門講解:
《RxJava用法入門及操作符講解,簡單易懂》
DownloadService downloadService = retrofit.create(DownloadService.class);
downloadService
.rxDownload("bytes=" + start + "-", url)
// 下載下傳過程在IO線程
.subscribeOn(Schedulers.io())
// 檔案轉換為位元組流,寫入檔案,也在IO線程
.observeOn(Schedulers.io())
.map(new Function<ResponseBody, InputStream>() {
@Override
public InputStream apply(ResponseBody responseBody) throws Exception {
return responseBody.byteStream();
}
})
.doOnNext(new Consumer<InputStream>() {
@Override
public void accept(InputStream inputStream) throws Exception {
// 儲存檔案
FileUtils.writeFileFromIS(filePath, fileName, inputStream, newFile);
}
})
// 主線程處理回調
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<InputStream>() {
@Override
public void onSubscribe(Disposable d) {
if (listener != null) {
listener.onStartDownload();
}
downloadInfo.setDisposable(d);
}
@Override
public void onNext(InputStream stream) {
}
@Override
public void onError(Throwable e) {
if (listener != null) {
listener.onFail(e.getMessage());
downloadInfo.setState(DownState.ERROR);
}
}
@Override
public void onComplete() {
downloadMap.remove(downloadInfo.getUrl());
}
});
下面是DownloadProgressListener#progress()方法裡監聽下載下傳進度及下載下傳完成的回調;
//切換到主線程
Disposable d = Observable.just(1)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<Integer>() {
@Override
public void accept(Integer integer) {
if (done) {
//下載下傳完成
listener.onFinishDownload(info.getSavePath() + File.separator + info.getFileName());
info.setState(DownState.FINISH);
} else {
//下載下傳進度回調
if (info.getContentLength() > 0) {
listener.onProgress(info.getReadLength(), info.getContentLength());
} else {
// 擷取不到檔案總大小(contentLength==0)
listener.onProgress(info.getReadLength(), -1);
}
}
}
});
源碼已上傳:https://github.com/liwuchen/SimpleDownload