溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Retrofit+RxJava實現(xiàn)帶進度下載文件

發(fā)布時間:2020-08-20 07:24:45 來源:腳本之家 閱讀:851 作者:jiashuai94 欄目:編程語言

Retrofit+RxJava已經是目前市場上最主流的網絡框架,使用它進行平常的網絡請求異常輕松,之前也用Retrofit做過上傳文件和下載文件,但發(fā)現(xiàn):使用Retrofit做下載默認是不支持進度回調的,但產品大大要求下載文件時顯示下載進度,那就不得不深究下了。

接下來我們一起封裝,使用Retrofit+RxJava實現(xiàn)帶進度下載文件。

github:JsDownload

先來看看UML圖:

Retrofit+RxJava實現(xiàn)帶進度下載文件大家可能還不太清楚具體是怎么處理的,別急,我們一步步來:

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、寫回調

/**
 * Description: 下載進度回調
 * Created by jia on 2017/11/30.
 * 人之所以能,是相信能
 */
public interface JsDownloadListener {

 void onStartDownload();

 void onProgress(int progress);

 void onFinishDownload();

 void onFail(String errorInfo);

}

這里就不用多說了,下載的回調,就至少應該有開始下載、下載進度、下載完成、下載失敗 四個回調方法。

注意下在onProgress方法中返回進度百分比,在onFail中返回失敗原因。

3、重寫ResponseBody,計算下載百分比

/**
 * Description: 帶進度 下載請求體
 * Created by jia on 2017/11/30.
 * 人之所以能,是相信能
 */
public class JsResponseBody extends ResponseBody {

 private ResponseBody responseBody;

 private JsDownloadListener downloadListener;

 // BufferedSource 是okio庫中的輸入流,這里就當作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;
 }
 };

 }
}

將網絡請求的ResponseBody 和JsDownloadListener 在構造中傳入。

這里的核心是source方法,返回ForwardingSource對象,其中我們重寫其read方法,在read方法中計算百分比,并將其傳給回調downloadListener。

4、攔截器

只封裝ResponseBody 是不夠的,關鍵我們需要拿到請求的ResponseBody ,這里我們就用到了攔截器Interceptor 。

/**
 * Description: 帶進度 下載 攔截器
 * 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();
 }
}

通常情況下攔截器用來添加,移除或者轉換請求或者回應的頭部信息。

在攔截方法intercept中返回我們剛剛封裝的ResponseBody 。

5、網絡請求service

/**
 * Description:
 * Created by jia on 2017/11/30.
 * 人之所以能,是相信能
 */
public interface DownloadService {

 @Streaming
 @GET
 Observable<ResponseBody> download(@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()改變調用它之前代碼的線程
 // observeOn()改變調用它之后代碼的線程
 retrofit.create(DownloadService.class)
 .download(url)
 .subscribeOn(Schedulers.io())
 .unsubscribeOn(Schedulers.io())
 .map(new Func1<ResponseBody, InputStream>() {

  @Override
  public InputStream call(ResponseBody responseBody) {
  return responseBody.byteStream();
  }
 })
 .observeOn(Schedulers.computation()) // 用于計算任務
 .doOnNext(new Action1<InputStream>() {
  @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");
 }

 }
}
  • 在構造中將下載地址和最后回調傳入,當然,也可以將保存地址傳入;
  • 在OkHttpClient添加我們自定義的攔截器;
  • 注意.addCallAdapterFactory(RxJavaCallAdapterFactory.create()) 支持RxJava;
  • 使用RxJava的map方法將responseBody轉為輸入流;
  • 在doOnNext中將輸入流寫入文件;

當然也需要注意下載回調的各個位置。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI