Retrofit+RxJava已經(jīng)是目前市場上最主流的網(wǎng)絡(luò)框架,使用它進(jìn)行平常的網(wǎng)絡(luò)請求異常輕松,之前也用Retrofit做過上傳文件和下載文件,但發(fā)現(xiàn):使用Retrofit做下載默認(rèn)是不支持進(jìn)度回調(diào)的,但產(chǎn)品大大要求下載文件時顯示下載進(jìn)度,那就不得不深究下了。
創(chuàng)新互聯(lián)專注于思明企業(yè)網(wǎng)站建設(shè),響應(yīng)式網(wǎng)站設(shè)計,商城網(wǎng)站定制開發(fā)。思明網(wǎng)站建設(shè)公司,為思明等地區(qū)提供建站服務(wù)。全流程按需網(wǎng)站設(shè)計,專業(yè)設(shè)計,全程項(xiàng)目跟蹤,創(chuàng)新互聯(lián)專業(yè)和態(tài)度為您提供的服務(wù)
接下來我們一起封裝,使用Retrofit+RxJava實(shí)現(xiàn)帶進(jìn)度下載文件。
github:JsDownload
先來看看UML圖:
大家可能還不太清楚具體是怎么處理的,別急,我們一步步來:
1、添依賴是必須的啦
compile 'io.reactivex:rxjava:1.1.0' compile 'io.reactivex:rxandroid:1.1.0' compile 'com.squareup.retrofit2:retrofit:2.0.0-beta4' compile 'com.squareup.retrofit2:converter-gson:2.0.0-beta4' compile 'com.squareup.retrofit2:adapter-rxjava:2.0.0-beta4'
使用時注意版本號
2、寫回調(diào)
/** * Description: 下載進(jìn)度回調(diào) * Created by jia on 2017/11/30. * 人之所以能,是相信能 */ public interface JsDownloadListener { void onStartDownload(); void onProgress(int progress); void onFinishDownload(); void onFail(String errorInfo); }
這里就不用多說了,下載的回調(diào),就至少應(yīng)該有開始下載、下載進(jìn)度、下載完成、下載失敗 四個回調(diào)方法。
注意下在onProgress方法中返回進(jìn)度百分比,在onFail中返回失敗原因。
3、重寫ResponseBody,計算下載百分比
/** * Description: 帶進(jìn)度 下載請求體 * Created by jia on 2017/11/30. * 人之所以能,是相信能 */ public class JsResponseBody extends ResponseBody { private ResponseBody responseBody; private JsDownloadListener downloadListener; // BufferedSource 是okio庫中的輸入流,這里就當(dāng)作inputStream來使用。 private BufferedSource bufferedSource; public JsResponseBody(ResponseBody responseBody, JsDownloadListener downloadListener) { this.responseBody = responseBody; this.downloadListener = downloadListener; } @Override public MediaType contentType() { return responseBody.contentType(); } @Override public long contentLength() { return responseBody.contentLength(); } @Override public BufferedSource source() { if (bufferedSource == null) { bufferedSource = Okio.buffer(source(responseBody.source())); } return bufferedSource; } private Source source(Source source) { return new ForwardingSource(source) { long totalBytesRead = 0L; @Override public long read(Buffer sink, long byteCount) throws IOException { long bytesRead = super.read(sink, byteCount); // read() returns the number of bytes read, or -1 if this source is exhausted. totalBytesRead += bytesRead != -1 ? bytesRead : 0; Log.e("download", "read: "+ (int) (totalBytesRead * 100 / responseBody.contentLength())); if (null != downloadListener) { if (bytesRead != -1) { downloadListener.onProgress((int) (totalBytesRead * 100 / responseBody.contentLength())); } } return bytesRead; } }; } }
將網(wǎng)絡(luò)請求的ResponseBody 和JsDownloadListener 在構(gòu)造中傳入。
這里的核心是source方法,返回ForwardingSource對象,其中我們重寫其read方法,在read方法中計算百分比,并將其傳給回調(diào)downloadListener。
4、攔截器
只封裝ResponseBody 是不夠的,關(guān)鍵我們需要拿到請求的ResponseBody ,這里我們就用到了攔截器Interceptor 。
/** * Description: 帶進(jìn)度 下載 攔截器 * Created by jia on 2017/11/30. * 人之所以能,是相信能 */ public class JsDownloadInterceptor implements Interceptor { private JsDownloadListener downloadListener; public JsDownloadInterceptor(JsDownloadListener downloadListener) { this.downloadListener = downloadListener; } @Override public Response intercept(Chain chain) throws IOException { Response response = chain.proceed(chain.request()); return response.newBuilder().body( new JsResponseBody(response.body(), downloadListener)).build(); } }
通常情況下攔截器用來添加,移除或者轉(zhuǎn)換請求或者回應(yīng)的頭部信息。
在攔截方法intercept中返回我們剛剛封裝的ResponseBody 。
5、網(wǎng)絡(luò)請求service
/** * Description: * Created by jia on 2017/11/30. * 人之所以能,是相信能 */ public interface DownloadService { @Streaming @GET Observabledownload(@Url String url); }
注意:
這里@Url是傳入完整的的下載URL;不用截取
使用@Streaming注解方法
6、最后開始請求
/** 1. Description: 下載工具類 2. Created by jia on 2017/11/30. 3. 人之所以能,是相信能 */ public class DownloadUtils { private static final String TAG = "DownloadUtils"; private static final int DEFAULT_TIMEOUT = 15; private Retrofit retrofit; private JsDownloadListener listener; private String baseUrl; private String downloadUrl; public DownloadUtils(String baseUrl, JsDownloadListener listener) { this.baseUrl = baseUrl; this.listener = listener; JsDownloadInterceptor mInterceptor = new JsDownloadInterceptor(listener); OkHttpClient httpClient = new OkHttpClient.Builder() .addInterceptor(mInterceptor) .retryOnConnectionFailure(true) .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) .build(); retrofit = new Retrofit.Builder() .baseUrl(baseUrl) .client(httpClient) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .build(); } /** * 開始下載 * * @param url * @param filePath * @param subscriber */ public void download(@NonNull String url, final String filePath, Subscriber subscriber) { listener.onStartDownload(); // subscribeOn()改變調(diào)用它之前代碼的線程 // observeOn()改變調(diào)用它之后代碼的線程 retrofit.create(DownloadService.class) .download(url) .subscribeOn(Schedulers.io()) .unsubscribeOn(Schedulers.io()) .map(new Func1() { @Override public InputStream call(ResponseBody responseBody) { return responseBody.byteStream(); } }) .observeOn(Schedulers.computation()) // 用于計算任務(wù) .doOnNext(new Action1 () { @Override public void call(InputStream inputStream) { writeFile(inputStream, filePath); } }) .observeOn(AndroidSchedulers.mainThread()) .subscribe(subscriber); } /** * 將輸入流寫入文件 * * @param inputString * @param filePath */ private void writeFile(InputStream inputString, String filePath) { File file = new File(filePath); if (file.exists()) { file.delete(); } FileOutputStream fos = null; try { fos = new FileOutputStream(file); byte[] b = new byte[1024]; int len; while ((len = inputString.read(b)) != -1) { fos.write(b,0,len); } inputString.close(); fos.close(); } catch (FileNotFoundException e) { listener.onFail("FileNotFoundException"); } catch (IOException e) { listener.onFail("IOException"); } } }
當(dāng)然也需要注意下載回調(diào)的各個位置。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持創(chuàng)新互聯(lián)。