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

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

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

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

(0)

相关推荐

  • RxJava2.x+ReTrofit2.x多线程下载文件的示例代码

    写在前面: 接到公司需求:要做一个apk升级的功能,原理其实很简单,百度也一大堆例子,可大部分都是用框架,要么就是HttpURLConnection,实在是不想这么干.正好看了两天的RxJava2.x+ReTrofit2.x,据说这俩框架是目前最火的异步请求框架了.固本文使用RxJava2.x+ReTrofit2.x实现多线程下载文件的功能. 如果对RxJava2.x+ReTrofit2.x不太了解的请先去看相关的文档. 大神至此请无视. 思路分析: 思路及其简洁明了,主要分为以下四步 1.获取

  • 基于Retrofit+Rxjava实现带进度显示的下载文件

    本文实例为大家分享了Retrofit Rxjava实现下载文件的具体代码,供大家参考,具体内容如下 本文采用 :retrofit + rxjava 1.引入: //rxJava compile 'io.reactivex:rxjava:latest.release' compile 'io.reactivex:rxandroid:latest.release' //network - squareup compile 'com.squareup.retrofit2:retrofit:latest

  • Retrofit Rxjava实现图片下载、保存并展示实例

    首先我们看一下Retrofit常规的用法,在不使用Rxjava的情况下,我们默认返回的是Call. public interface ServiceApi { //下载文件 @GET Call<ResponseBody> downloadPicFromNet(@Url String fileUrl); } 但是如果我们要配合Rxjava使用,那么就要按照如下方式来重新定义我们的方法: @GET Observable<ResponseBody> downloadPicFromNet(

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

    Retrofit+RxJava已经是目前市场上最主流的网络框架,使用它进行平常的网络请求异常轻松,之前也用Retrofit做过上传文件和下载文件,但发现:使用Retrofit做下载默认是不支持进度回调的,但产品大大要求下载文件时显示下载进度,那就不得不深究下了. 接下来我们一起封装,使用Retrofit+RxJava实现带进度下载文件. github:JsDownload 先来看看UML图: 大家可能还不太清楚具体是怎么处理的,别急,我们一步步来: 1.添依赖是必须的啦 compile 'io.

  • RxJava+Retrofit+OkHttp实现多文件下载之断点续传

    背景 断点续传下载一直是移动开发中必不可少的一项重要的技术,同样的Rxjava和Retrofit的结合让这个技术解决起来更加的灵活,我们完全可以封装一个适合自的下载框架,简单而且安全! 效果 实现 下载和之前的http请求可以相互独立,所以我们单独给download建立一个工程moudel处理 1.创建service接口 和以前一样,先写接口 注意:Streaming是判断是否写入内存的标示,如果小文件可以考虑不写,一般情况必须写:下载地址需要通过@url动态指定(不适固定的),@head标签是

  • Retrofit+Rxjava实现文件上传和下载功能

    Retrofit简介: 在Android API4.4之后,Google官方使用了square公司推出的okHttp替换了HttpClient的请求方式.后来square公司又推出了基于okHttp的网络请求框架:Retrofit. 什么是 RxJava? RxJava 是一个响应式编程框架,采用观察者设计模式.所以自然少不了 Observable 和 Subscriber 这两个东东了. RxJava 是一个开源项目,地址:https://github.com/ReactiveX/RxJava

  • Retrofit+Rxjava下载文件进度的实现

    前言 最近在学习Retrofit,虽然Retrofit没有提供文件下载进度的回调,但是Retrofit底层依赖的是OkHttp,实际上所需要的实现OkHttp对下载进度的监听,在OkHttp的官方Demo中,有一个Progress.java的文件,顾名思义.点我查看. 准备工作 本文采用Dagger2,Retrofit,RxJava. compile'com.squareup.retrofit2:retrofit:2.0.2' compile'com.squareup.retrofit2:con

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

    项目中需要使用到更新版本,因此研究了一下Retrofit的下载文件,和进度条效果,其间也遇到了一些坑,写出来加深一下记忆,也为别的同学提供一下思路. 先说一下版本控制吧,通用做法基本上是通过接口获取服务器存储的app版本号,与应用的版本号进行比较,版本较低就去更新,先看一下如何获取应用版本号吧 PackageManager packageManager = mActivity.getPackageManager(); PackageInfo packageInfo = null; try { p

  • springMVC实现前台带进度条文件上传的示例代码

    项目框架采用spring+hibernate+springMVC如果上传文件不想使用flash那么你可以采用HTML5;截图前段模块是bootstarp框架;不废话直接来代码;spring-mvc配置文件;效果截图如下: 详细实现如下: 1.mvc-config.xml <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/s

  • 使用Retrofit下载文件并实现进度监听的示例

    1.前言 最近要做一个带进度条下载文件的功能,网上看了一圈,发现好多都是基于 OkHttpClient 添加拦截器来实现的,个人觉得略显复杂,所以还是采用最简单的方法来实现:基于文件写入来进行进度的监听. 2.实现步骤 2.1 设计监听接口 根据需求设计一下接口: public interface DownloadListener { void onStart();//下载开始 void onProgress(int progress);//下载进度 void onFinish(String p

  • android中实现OkHttp下载文件并带进度条

    OkHttp是比较火的网络框架,它支持同步与异步请求,支持缓存,可以拦截,更方便下载大文件与上传文件的操作.下面我们用OkHttp来下载文件并带进度条! 相关资料: 官网地址:http://square.github.io/okhttp/ github源码地址:https://github.com/square/okhttp 一.服务器端简单搭建 可以参考搭建本地Tomcat服务器及相关配置这篇文章. 新建项目OkHttpServer,在WebContent目录下新建downloadfile目录

  • asp.net mvc 实现文件上传带进度条的思路与方法

    前言 文件上传与下载的操作在实际项目中经常是很重要的一个内容,在使用ASP.NET Web Form的时候我们可以使用诸多的服务器控件,FileIpload就是其中之一,但是在ASP.NET不建议使用那些服务器控件,因为那样违反三层架构原则.最近参考网络资料,学习了ASP.NET MVC如何上传文件. 而这篇文章主要重点是asp.net mvc 实现文件上传带进度条,下面来一起看看吧. 实现思路 ajax异步上传文件,且开始上传文件的时候启动轮询来实时获取文件上传进度.保存进度我采用的是memc

  • asp.net单文件带进度条上传的解决方案

    最近做项目中遇到很多问题,比如带进度条的文件上传,看了网上很多资料还没找到真正意义上的ASP.NET实现进度条上传(可能是我没找到),下面我来跟大家分享一下我实现的这个程序. 首先看下界面效果,当然你可以完全修改界面为你自己所用. 先解释一下这个程序,该程序采用了jquery框架,实现了小文件上传,不超过80Mb,可以在web.config文件中进行相应的配置,但是有个最大值,具体需要查看msdn.开发环境采用visual studio 2013 .net framework 4.5,运行的时候

随机推荐