天天看點

Retrofit+okHttp+RxJava打造一款簡單易用的Android下載下傳架構

SimpleDownload 一款Android下載下傳架構的講解

      • 一、本架構實作的功能特性
      • 二、最終效果圖,及使用方法
      • 三、關鍵功能源碼說明
        • 1.檔案下載下傳和普通網絡請求的不同
        • 2. 斷點續傳如何實作
        • 3. 如何得到檔案下載下傳進度
        • 4. 如何把回調切換到主線程

本文介紹自己封裝的一套下載下傳架構。

首先要知道,檔案下載下傳和我們平時調接口一樣,要發起網絡請求的,是以我們先找個合适的網絡請求庫,這裡我們選擇Retrofit+OkHttp的組合,為了友善支援回調線程切換等功能再加上RxJava的組合。

本架構已更新多次,而本文章可能未及時更新。詳細介紹說明請看github

https://github.com/liwuchen/SimpleDownload

一、本架構實作的功能特性

本下載下傳架構實作了這樣的特性:

1. 支援斷點續傳。即下載下傳斷開後,支援從已完成的部分開始繼續下載下傳。

2. 支援多個任務同時下載下傳。

3. 有各種情況的回調。如下載下傳進度回調,任務取消、完成等回調。

4. 下載下傳任務在子線程進行,回調在主線程。不需要使用者自己切換線程。

5. 使用者可以自定義檔案儲存路徑和檔案名。

二、最終效果圖,及使用方法

Retrofit+okHttp+RxJava打造一款簡單易用的Android下載下傳架構

本架構源碼及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);
           

三、關鍵功能源碼說明

本架構的源碼類如下:

Retrofit+okHttp+RxJava打造一款簡單易用的Android下載下傳架構

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