Retrofit+RxJava实现带进度下载文件

网友投稿 248 2023-08-05

Retrofit+RxJava实现带进度下载文件

Retrofit+Rxjava已经是目前市场上最主流的网络框架,使用它进行平常的网络请求异常轻松,之前也用Retrofit做过上传文件和下载文件,但发现:使用Retrofit做下载默认是不支持进度回调的,但产品大大要求下载文件时显示下载进度,那就不得不深究下了。

接下来我们一起封装,使用Retrofit+RxJava实现带进度下载文件。

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、写回调

/**

* Description: 下载进度回调

* Created by jia on 2017/11/30.

* 人之所以能,是相信能

*/

public interface JsDownloadListener {

void onStartDownload();

void onProgress(int progress);

void onFinishDownload()PDSFNKSEbL;

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 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;

privatPDSFNKSEbLe 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() {

@Override

public InputStream call(ResponseBody responseBody) {

return responseBody.byteStream();

}

})

.observeOn(Schedulers.computation()) // 用于计算任务

.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");

}

}

}

在构造中将下载地址和最后回调传入,当然,也可以将保存地址传入;

在OkHttpClient添加我们自定义的拦截器;

注意.addCallAdapterFactory(RxJavaCallAdapterFactory.create()) 支持RxJava;

使用RxJava的map方法将responseBody转为输入流;

在doOnNext中将输入流写入文件;

当然也需要注意下载回调的各个位置。

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:nuxt框架中路由鉴权之Koa和Session的用法
下一篇:node puppeteer(headless chrome)实现网站登录
相关文章

 发表评论

暂时没有评论,来抢沙发吧~