欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2023-12-11 17:17:52
retrofit+rxjava已经是目前市场上最主流的网络框架,使用它进行平常的网络请求异常轻松,之前也用retrofit做过上传文件和下载文件,但发现:使用retrofi...

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

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

github:jsdownload

先来看看uml图:

Retrofit+RxJava实现带进度下载文件大家可能还不太清楚具体是怎么处理的,别急,我们一步步来:

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中将输入流写入文件;

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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。

上一篇:

下一篇: