Python实战社群

Java实战社群

长按识别下方二维码,按需求添加

扫码关注添加客服

进Python社群▲

扫码关注添加客服

进Java社群

作者丨不怕天黑

来源丨掘金

https://juejin.im/post/5ded221a518825125d14a1d4

1、前言

这期间,一直有人问我,retrofit不香吗?

之前不知道该如何回答这个问题,现在我想说,香!!retrofit前综合得分最高的选手,但它也有它的不足。

RxHttp相较于retrofit,功能上,两者均能实现,并无多大差异,更多的差异体现功能的使用上,也就是易用性,如对文件上传/下载/进度监听的操作上,RxHttp用及简的API,可以说碾压retrofit;

另外在baseUrl、公共参数/请求头、请求加解密等功能上的易用性都要优于retrofit;然而这些,个人觉得都不算什么,个人觉得RxHttp最大的优势在于它近乎为0的上手成本、及简的API以及高扩展性,看完这篇文章,相信你会有同感。

那RxHttp就没有缺点吗?

有,那就是它的稳定性目前还不如retrofit,毕竟RxHttp刚出道8个月,且全部是我一个人在维护,当然,并不是说RxHttp不稳定,RxHttp未开源前,在我司的项目已经使用了近2年,接着今年4月份将其开源,至今大大小小已迭代20多个版本,目前用的人也不在少数,可以说很稳定了。

2、简介

RxHttp是基于OkHttp的二次封装,并与RxJava做到无缝衔接,一条链就能发送任意请求。主要优势如下:

1. 支持Gson、Xml、ProtoBuf、FastJson等第三方数据解析工具

2. 支持Get、Post、Put、Delete等任意请求方式,可自定义请求方式

3. 支持在Activity/Fragment/View/ViewModel/任意类中,自动关闭请求

4. 支持统一加解密,且可对单个请求设置是否加解密

5. 支持添加公共参数/头部,且可对单个请求设置是否添加公共参数/头部

6. 优雅的实现文件上传/下载及进度的监听,且支持断点下载

7. 优雅的对错误统一处理,且不打破Lambda表达式

8. 优雅的处理多个BaseUrl及动态BaseUrl

9. 30秒即可上手,学习成本极低

gradle依赖

implementation 'com.rxjava.rxhttp:rxhttp:1.3.4'
//注解处理器,生成RxHttp类,即可一条链发送请求
annotationProcessor 'com.rxjava.rxhttp:rxhttp-compiler:1.3.4'
//管理RxJava及生命周期,Activity/Fragment 销毁,自动关闭未完成的请求
implementation 'com.rxjava.rxlife:rxlife:1.1.0'//非必须 根据自己需求选择Converter  RxHttp默认内置了GsonConverter
implementation 'com.rxjava.rxhttp:converter-jackson:1.3.4'
implementation 'com.rxjava.rxhttp:converter-fastjson:1.3.4'
implementation 'com.rxjava.rxhttp:converter-protobuf:1.3.4'
implementation 'com.rxjava.rxhttp:converter-simplexml:1.3.4'

注:kotlin用户,请使用kapt替代annotationProcessor

3、使用

3.1、准备工作

RxHttp 要求项目使用Java 8,请在 app 的 build.gradle 文件中添加以下代码

compileOptions {sourceCompatibility JavaVersion.VERSION_1_8targetCompatibility JavaVersion.VERSION_1_8
}

此时,再Rebuild一下项目(通过Rebuild生成RxHttp类),就可以开始RxHttp的入坑之旅

3.2、配置默认的BaseUrl

通过@DefaultDomain注解配置默认域名,如下:

public class Url {@DefaultDomain //设置为默认域名public static String baseUrl = "https://www.wanandroid.com/";
}

此步骤是非必须的,这里先介绍@DefaultDomain注解的用法,更多有关域名的介绍,请查看本文3.6章节----多域名/动态域名

3.3、请求三部曲

先来看看如何发送一个最简单的请求,如下

RxHttp.get("http://...")  //第一步, 通过get、postXxx、putXxx等方法,确定请求类型         .asString()           //第二步, 通过asXxx系列方法,确定返回数据类型    .subscribe(s -> {     //第三步, 订阅回调(此步骤同RxJava订阅观察者)//请求成功                                         }, throwable -> {                                  //请求失败                                         });                                                

是的,不用怀疑,就是这么简单,重要的事情说3遍

任意请求,任意返回数据类型,皆遵循请求三部曲

任意请求,任意返回数据类型,皆遵循请求三部曲

任意请求,任意返回数据类型,皆遵循请求三部曲

到这,你已经掌握了RxHttp的精髓,我们只需牢记请求三部曲,使用RxHttp就会得心应手。

3.3.1、第一部曲:确定请求类型

RxHttp内部共提供了14个请求方法,如下:

RxHttp.get(String)              //get请求    参数拼接在url后面
RxHttp.head(String)             //head请求   参数拼接在url后面
RxHttp.postForm(String)         //post请求   参数以{application/x-www-form-urlencoded}形式提交
RxHttp.postJson(String)         //post请求   参数以{application/json; charset=utf-8}形式提交,发送Json对象
RxHttp.postJsonArray(String)    //post请求   参数以{application/json; charset=utf-8}形式提交,发送Json数组
RxHttp.putForm(String)          //put请求    参数以{application/x-www-form-urlencoded}形式提交
RxHttp.putJson(String)          //put请求    参数以{application/json; charset=utf-8}形式提交,发送Json对象
RxHttp.putJsonArray(String)     //put请求    参数以{application/json; charset=utf-8}形式提交,发送Json数组
RxHttp.patchForm(String)        //patch请求  参数以{application/x-www-form-urlencoded}形式提交
RxHttp.patchJson(String)        //patch请求  参数以{application/json; charset=utf-8}形式提交,发送Json对象
RxHttp.patchJsonArray(String)   //patch请求  参数以{application/json; charset=utf-8}形式提交,发送Json数组
RxHttp.deleteForm(String)       //delete请求 参数以{application/x-www-form-urlencoded}形式提交
RxHttp.deleteJson(String)       //delete请求 参数以{application/json; charset=utf-8}形式提交,发送Json对象
RxHttp.deleteJsonArray(String)  //delete请求 参数以{application/json; charset=utf-8}形式提交,发送Json数组

以上14个请求方法你会发现,其实就6个类型,分别对应是Get、Head、Post、Put、Patch、Delete方法,只是其中Post、Put、Patch、Delete各有3个方法有不同形式的提交方式,只需要根据自己的需求选择就好。

如以上方法还不能满足你的需求,我们还可以通过@Param注解自定义请求方法,有关注解的使用,本文后续会详细介绍。

注:当调用xxxForm方法发送请求时,通过setMultiForm()方法或者调用addFile(String, File)添加文件时,内部会自动将参数以{multipart/form-data}方式提交

添加参数/请求头确定请求方法后,我们就可以调用一系列addXxx()方法添加参数/请求头,如下:

RxHttp.get("/service/...")       //发送get请求.add("key", "value")         //添加参数.addAll(new HashMap<>())     //通过Map添加多个参数.addHeader("deviceType", "android")     //添加请求头...

任意请求,都可调用以上3个方法添加参数/请求头,当然,在不同的请求方式下,也会有不同的addXxx方法供开发者调用。

如下:

//postJson请求方法下会有更多addAll等方法可供调用
RxHttp.postJson("/service/...") //发送post Json请求.addAll(new JsonObject())   //通过json对象添加多个参数.addAll("{\"height\":180,\"weight\":70}") //通过json字符串添加多个参数...//postForm请求方法下会有一系列addFile方法可供调用
RxHttp.postForm("/service/...")  //发送post表单请求.addFile("file", new File("xxx/1.png")) //添加单个文件.addFile("fileList", new ArrayList<>()) //添加多个文件...

以上只列出了几个常用的addXxx方法,更多方法请下载源码体验。

3.3.2、第二部曲:确定返回数据类型

添加好参数/请求头后,正式进入第二部曲,确定返回数据类型,我们通过asXxx方法确定返回类型,比如,我们要返回一个Student对象,就可以通过asObject(Class<T>)方法,如下:

RxHttp.postForm("/service/...")  //发送post表单请求.add("key", "value")         //添加参数,可调用多次.asObject(Student.class)    //返回Student类型.subscribe(student -> {   //请求成功,这里就能拿到 Student对象               }, throwable -> {         //请求失败                });    

如果要返回Student对象列表,则可以通过asList(Class<T>)方法,如下:

RxHttp.postForm("/service/...")  //发送post表单请求.add("key", "value")         //添加参数,可调用多次.asList(Student.class)       //返回List<Student>类型.subscribe(students -> {   //请求成功,这里就能拿到 Student对象列表               }, throwable -> {         //请求失败                });    

解析Response<T>类型数据然而,现实开发中,大多数人的接口,返回的数据结构都类似下面的这个样子

public class Response<T> {private int    code;private String msg;private T      data;//这里省略get、set方法
}

对于这种数据结构,按传统的写法,每次都要对code做判断,如果有100个请求,就要判断100次,真的会逼死强迫症患者。

RxHttp对于这种情况,给出完美的答案,比如Response<T>里面的T代表一个Student对象,则可以通过asResponse(Class<T>)方法获取,如下:

RxHttp.postForm("/service/...")   //发送post表单请求.add("key", "value")          //添加参数,可调用多次.asResponse(Student.class)    //返回Student类型.subscribe(student -> {   //请求成功,这里能拿到 Student对象               }, throwable -> {         //请求失败                });    

如果Response<T>里面的T代表一个List<Student>列表对象,则可以通过asResponseList(Class<T>)方法获取,如下

RxHttp.postForm("/service/...")   //发送post表单请求.add("key", "value")          //添加参数,可调用多次.asResponseList(Student.class)    //返回List<Student>类型.subscribe(students -> {   //请求成功,这里能拿到List<Student>列表对象               }, throwable -> {         //请求失败                });    

更多时候,我们的列表数据是分页的,类似下面的数据结构

{"code": 0,"msg": "","data": {"totalPage": 0,"list": []}
}

此时,调用RxHttp的asResponsePageList(Class<T>)方法依然可以完美解决,如下:

RxHttp.postForm("/service/...")   //发送post表单请求.add("key", "value")          //添加参数,可调用多次.asResponsePageList(Student.class)    //返回PageList<Student>类型.subscribe(pageList -> {   //请求成功,这里能拿到PageList<Student>列表对象 int totalPage = pageList.getTotalPage();   //总页数List<Student> students = pageList.getData();  //单页列表数据        }, throwable -> {         //请求失败                });    

到这,估计很多人会问我:

  • 你的code在哪里判断的?

  • 我的code是100或者其它值才代表正确,怎么改?

  • 我的Response<T>类里面的字段名,跟你的都不一样,怎么该?

  • 你这成功的时候直接返回Response<T>里面的T,那我还要拿到code做其他的判断,执行不同业务逻辑,怎么办?

这里可以先告诉大家,asResponse(Class<T>)、asResponseList(Class<T>)、asResponsePageList(Class<T>)这3个方法并不是RxHttp内部提供的,而是通过自定义解析器生成,里面的code判断、Response<T>类都是开发者自定义的,如何自定义解析器,请查看本文5.1章节----自定义Parser。

接着回答第4个问题,如何拿到code做其他的业务逻辑判断,很简单,我们只需用OnError接口处理错误回调即可,如下:

RxHttp.postForm("/service/...")   //发送post表单请求.add("key", "value")          //添加参数,可调用多次.asResponse(Student.class)    //返回Student类型.subscribe(student -> {   //请求成功,这里能拿到 Student对象               }, (OnError) error -> {     //注意,这里要用OnError接口,其中error是一个ErrorInfo对象  //失败回调//拿到code字段,此时就可以对code做判断,执行不同的业务逻辑 int code = error.getErrorCode();     String errorMsg = error.getErrorMsg()  //拿到msg字段             });    
注:上面的OnError接口并非是RxHttp内部提供的,而是自定义的,在Demo里可以找到

以上介绍的5个asXxx方法,可以说基本涵盖80%以上的业务场景,接下来我们看看RxHttp都提供了哪些asXxx方法,如下:

RxHttp内部共提供了23个asXXX方法,其中:

  • 有7个是返回基本类型的包装类型,如:asInteger、asBoolean、asLong等等;

  • 还有7个是返回对象类型,如:asString、asBitmap、asList、asMap(3个)以及最常用asObject方法;

  • 剩下9个是asParser(Parser<T>)、asUpload系列方法及asDownload系列方法。

duang、duang、duang !!! 划重点,这里我可以告诉大家,其实前面的14个方法,最终都是通过asParser(Parser<T>)方法实现的,具体实现过程,这里先跳过,后续会详细讲解。

3.3.3、第三部曲:订阅回调

这一步就很简单了,在第二部曲中,asXxx方法会返回Observable<T>对象,没错,就是RxJava内部的Observable<T>对象,此时我们便可通过subscribe系列方法订阅回调,如下:

//不处理任何回调
RxHttp.postForm("/service/...")   //发送post表单请求.add("key", "value")          //添加参数,可调用多次.asResponseList(Student.class)    //返回List<Student>类型.subscribe();    //不订阅任何回调//仅订阅成功回调
RxHttp.postForm("/service/...")   //发送post表单请求.add("key", "value")          //添加参数,可调用多次.asResponseList(Student.class)    //返回List<Student>类型.subscribe(students -> {   //请求成功,这里能拿到List<Student>列表对象               });    //订阅成功与失败回调
RxHttp.postForm("/service/...")   //发送post表单请求.add("key", "value")          //添加参数,可调用多次.asResponseList(Student.class)    //返回List<Student>类型.subscribe(students -> {   //请求成功,这里能拿到List<Student>列表对象               }, throwable -> {         //请求失败                });//等等,省略

另外,我们还可以订阅请求开始/结束的回调,如下:

RxHttp.get("/service/...").asString().observeOn(AndroidSchedulers.mainThread()).doOnSubscribe(disposable -> {//请求开始,当前在主线程回调}).doFinally(() -> {//请求结束,当前在主线程回调}).as(RxLife.as(this))  //感知生命周期.subscribe(pageList -> {//成功回调,当前在主线程回调}, (OnError) error -> {//失败回调,当前在主线程回调});

到这,请求三部曲介绍完毕,接着,将介绍其它常用的功能

3.4、初始化

//设置debug模式,默认为false,设置为true后,发请求,过滤"RxHttp"能看到请求日志
RxHttp.setDebug(boolean debug)
//非必须,只能初始化一次,第二次将抛出异常
RxHttp.init(OkHttpClient okHttpClient)
//或者,调试模式下会有日志输出
RxHttp.init(OkHttpClient okHttpClient, boolean debug)

此步骤是非必须的,如需要添加拦截器等其他业务需求,则可调用init方法进行初始化,不初始化或者传入null即代表使用默认OkHttpClient对象,建议在Application中初始化,默认的OkHttpClient对象在HttpSender类中可以找到,如下:

private static OkHttpClient getDefaultOkHttpClient() {                              X509TrustManager trustAllCert = new X509TrustManagerImpl();                     SSLSocketFactory sslSocketFactory = new SSLSocketFactoryImpl(trustAllCert);     return new OkHttpClient.Builder()                                               .connectTimeout(10, TimeUnit.SECONDS)                                       .readTimeout(10, TimeUnit.SECONDS)                                          .writeTimeout(10, TimeUnit.SECONDS)                                         .sslSocketFactory(sslSocketFactory, trustAllCert) //添加信任证书                  .hostnameVerifier((hostname, session) -> true) //忽略host验证                   .build();
}                                                                                   

虽然初始化是非必须的,但是建议大家传入自定义的OkHttpClient对象,一来,自定义的OkHttpClient能最大化满足自身的业务;二来,随着RxHttp版本的升级,默认的OkHttpClient可能会发生变化(虽然可能性很小),故建议自定义OkHttpClient对象传入RxHttp。

3.5、公共参数/请求头

RxHttp支持为所有的请求添加公共参数/请求头,当然,如果你希望某个请求不添加公共参数/请求头,也是支持的,而且非常简单。如下:

RxHttp.setOnParamAssembly(new Function() {@Overridepublic Param apply(Param p) { //此方法在子线程中执行,即请求发起线程Method method = p.getMethod();if (method.isGet()) {     //可根据请求类型添加不同的参数} else if (method.isPost()) {}return p.add("versionName", "1.0.0")//添加公共参数.addHeader("deviceType", "android"); //添加公共请求头}
});

我们需要调用RxHttp.setOnParamAssembly(Function)方法,并传入一个Function接口对象,每次发起请求,都会回调该接口。

当然,如果希望某个请求不回调该接口,即不添加公共参数/请求头,则可以调用setAssemblyEnabled(boolean)方法,并传入false即可,如下:

RxHttp.get("/service/...")       //get请求 .setAssemblyEnabled(false)   //设置是否添加公共参数/头部,默认为true    .asString()                  //返回字符串数据    .subscribe(s -> {            //这里的s为String类型//请求成功                                         }, throwable -> {                                  //请求失败                                         });                                                

3.6、多域名/动态域名

3.6.1、多域名

现实开发中,我们经常会遇到多个域名的情况,其中1个为默认域名,其它为非默认域名,对于这种情况,RxHttp提供了@DefaultDomain()、@Domain()这两个注解来标明默认域名和非默认域名,如下:

public class Url {@DefaultDomain() //设置为默认域名public static String baseUrl = "https://www.wanandroid.com/"@Domain(name = "BaseUrlBaidu") //非默认域名,并取别名为BaseUrlBaidupublic static String baidu = "https://www.baidu.com/";@Domain(name = "BaseUrlGoogle") //非默认域名,并取别名为BaseUrlGooglepublic static String google = "https://www.google.com/";
}

通过@Domain()注解标注非默认域名,就会在RxHttp类中生成setDomainToXxxIfAbsent()方法,其中Xxx就是注解中取的别名。

上面我们使用了两个@Domain()注解,此时(需要Rebuild一下项目)就会在RxHttp类中生成setDomainToBaseUrlBaiduIfAbsent()、setDomainToBaseUrlGoogleIfAbsent()这两方法,此时发请求,我们就可以使用指定的域名,如下:

//使用默认域名,则无需添加任何额外代码
//此时 url = "https://www.wanandroid.com/service/..."
RxHttp.get("/service/...").asString()  .subscribe();//手动输入域名,此时 url = "https://www.mi.com/service/..."
RxHttp.get("https://www.mi.com/service/...").asString()  .subscribe();//手动输入域名时,若再次指定域名,则无效
//此时 url = "https://www.mi.com/service/..."
RxHttp.get("https://www.mi.com/service/...").setDomainToBaseUrlBaiduIfAbsent()  //此时指定Baidu域名无效.asString()  .subscribe();//使用谷歌域名,此时 url = "https://www.google.com/service/..."
RxHttp.get("/service/...").setDomainToBaseUrlGoogleIfAbsent() //指定使用Google域名.asString()  .subscribe();

通过以上案例,可以知道,RxHttp共有3种指定域名的方式,按优先级排名分别是:手动输入域名 > 指定非默认域名 > 使用默认域名。

3.6.2、动态域名

现实开发中,也会有动态域名切换的需求,如域名被封、或者需要根据服务端下发的域名去配置,这对于RxHttp来说简直就是 so easy !!! 我们只需要对BaseUrl重新赋值,此时发请求便会立即生效,如下:

//此时 url = "https://www.wanandroid.com/service/..."
RxHttp.get("/service/...").asString()  .subscribe();Url.baseUrl = "https://www.qq.com"; //动态更改默认域名,改完立即生效,非默认域名同理
//此时 url = "https://www.qq.com/service/..."
RxHttp.get("/service/...").asString()  .subscribe();

3.7、关闭请求

我们知道,在Activity/Fragment中发起请求,如果页面销毁时,请求还未结束,就会有内存泄漏的危险,因此,我们需要在页面销毁时,关闭一些还未完成的请求,RxHttp提供了两种关闭请求的方式,分别是自动+手动。

3.7.1、自动关闭请求

自动关闭请求,需要引入本人开源的另一个库RxLife,先来看看如何用:

https://github.com/liujingxing/RxLife

//以下代码均在FragmentActivty/Fragment中调用RxHttp.postForm("/service/...").asString().as(RxLife.as(this)) //页面销毁、自动关闭请求.subscribe();//或者
RxHttp.postForm("/service/...").asString().as(RxLife.asOnMain(this)) //页面销毁、自动关闭请求 并且在主线程回调观察者.subscribe();//kotlin用户,请使用life或lifeOnMain方法,如下:
RxHttp.postForm("/service/...").asString().life(this) //页面销毁、自动关闭请求.subscribe();//或者
RxHttp.postForm("/service/...").asString().lifeOnMain(this) //页面销毁、自动关闭请求 并且在主线程回调观察者.subscribe();

上面的this为LifecycleOwner接口对象,我们的FragmentActivity/Fragment均实现了这个接口,所有我们在FragmentActivity/Fragment中可以直接传this。

3.7.2、手动关闭请求

手动关闭请求,我们只需要在订阅回调的时候拿到Disposable对象,通过该对象可以判断请求是否结束,如果没有,就可以关闭请求,如下:

//订阅回调,可以拿到Disposable对象
Disposable disposable = RxHttp.get("/service/...").asString()  .subscribe(s -> { //成功回调}, throwable -> {//失败回调});if (!disposable.isDisposed()) {  //判断请求有没有结束disposable.dispose();       //没有结束,则关闭请求
}                              

3.8、文件上传/下载/进度监听

RxHttp可以非常优雅的实现上传/下载及进度的监听,是骡子是马,拉出来溜溜

3.8.1上传

通过addFile系列方法添加文件,如下:

RxHttp.postForm("/service/...") //发送Form表单形式的Post请求  .addFile("file1", new File("xxx/1.png"))  //添加单个文件      .addFile("fileList", new ArrayList<>())   //通过List对象,添加多个文件     .asString()                                      .subscribe(s -> {                              //上传成功                                     }, throwable -> {                              //上传失败                                     });                                            

通过asUpload系列方法监听上传进度,如下:

RxHttp.postForm("/service/...") //发送Form表单形式的Post请求                                    .addFile("file1", new File("xxx/1.png"))                                         .addFile("file2", new File("xxx/2.png"))                                         .asUpload(progress -> {                                                          //上传进度回调,0-100,仅在进度有更新时才会回调                                                  int currentProgress = progress.getProgress(); //当前进度 0-100                   long currentSize = progress.getCurrentSize(); //当前已上传的字节大小                   long totalSize = progress.getTotalSize();     //要上传的总字节大小                    }, AndroidSchedulers.mainThread())   //指定回调(进度/成功/失败)线程,不指定,默认在请求所在线程回调                                           .subscribe(s -> {                                                                //上传成功                                                                       }, throwable -> {                                                                //上传失败                                                                       });                                                                              

可以看到,跟上传的代码相比,我们仅仅是使用了asUpload(Consumer, Scheduler)方法替换asString()方法,第一个参数是进度监听接口,每当进度有更新时,都会回调该接口,第二个参数是指定回调的线程,这里我们指定了在UI线程中回调。

3.8.2、下载

下载使用asDownload(String)方法,传入本地路径即可

  //文件存储路径
String destPath = getExternalCacheDir() + "/" + System.currentTimeMillis() + ".apk";
RxHttp.get("http://update.9158.com/miaolive/Miaolive.apk").asDownload(destPath) //注意这里使用asDownload操作符,并传入本地路径.subscribe(s -> {//下载成功,回调文件下载路径}, throwable -> {//下载失败});

3.8.3、带进度下载

带进度下载使用asDownload(String,Consumer,Scheduler)方法

  //文件存储路径
String destPath = getExternalCacheDir() + "/" + System.currentTimeMillis() + ".apk";
RxHttp.get("http://update.9158.com/miaolive/Miaolive.apk").asDownload(destPath, progress -> {//下载进度回调,0-100,仅在进度有更新时才会回调,最多回调101次,最后一次回调文件存储路径int currentProgress = progress.getProgress(); //当前进度 0-100long currentSize = progress.getCurrentSize(); //当前已下载的字节大小long totalSize = progress.getTotalSize();     //要下载的总字节大小}, AndroidSchedulers.mainThread()) //指定主线程回调.subscribe(s -> {//s为String类型,这里为文件存储路径//下载完成,处理相关逻辑}, throwable -> {//下载失败,处理相关逻辑});

3.8.4、断点下载

断点下载相较于下载,仅需要调用setRangeHeader(long startIndex, long endIndex)方法传入开始及结束位置即可(结束位置不传默认为文件末尾),其它没有任何差别

String destPath = getExternalCacheDir() + "/" + "Miaobo.apk";
long length = new File(destPath).length(); //已下载的文件长度
RxHttp.get("http://update.9158.com/miaolive/Miaolive.apk").setRangeHeader(length)  //设置开始下载位置,结束位置默认为文件末尾.asDownload(destPath).subscribe(s -> { //s为String类型//下载成功,处理相关逻辑}, throwable -> {//下载失败,处理相关逻辑});

3.8.5、带进度断点下载

带进度断点下载相较于带进度下载仅需要调用setRangeHeader方法传入开始及结束位置即可(结束位置不传默认为文件末尾),其它没有任何差别

String destPath = getExternalCacheDir() + "/" + "Miaobo.apk";
long length = new File(destPath).length(); //已下载的文件长度
RxHttp.get("http://update.9158.com/miaolive/Miaolive.apk").setRangeHeader(length)  //设置开始下载位置,结束位置默认为文件末尾.asDownload(destPath, progress -> {//下载进度回调,0-100,仅在进度有更新时才会回调int currentProgress = progress.getProgress(); //当前进度 0-100long currentSize = progress.getCurrentSize(); //当前已下载的字节大小long totalSize = progress.getTotalSize();     //要下载的总字节大小}, AndroidSchedulers.mainThread()) //指定主线程回调.subscribe(s -> { //s为String类型//下载成功,处理相关逻辑}, throwable -> {//下载失败,处理相关逻辑});

注:上面带进度断点下载中,返回的进度会从0开始,如果需要衔接上次下载的进度,则调用asDownload(String,long,Consumer,Scheduler)方法传入上次已经下载好的长度(第二个参数),如下:

String destPath = getExternalCacheDir() + "/" + "Miaobo.apk";
long length = new File(destPath).length(); //已下载的文件长度
RxHttp.get("http://update.9158.com/miaolive/Miaolive.apk").setRangeHeader(length)  //设置开始下载位置,结束位置默认为文件末尾.asDownload(destPath, length, progress -> {//下载进度回调,0-100,仅在进度有更新时才会回调int currentProgress = progress.getProgress(); //当前进度 0-100long currentSize = progress.getCurrentSize(); //当前已下载的字节大小long totalSize = progress.getTotalSize();     //要下载的总字节大小}, AndroidSchedulers.mainThread()) //指定主线程回调.subscribe(s -> { //s为String类型//下载成功,处理相关逻辑}, throwable -> {//下载失败,处理相关逻辑});

为了节省一下篇幅,让出源码解析的内容,这里省略了一些配置内容,也省略了一些扩展但是,但不影响整体阅读:

3.9、超时设置

3.10、设置Converter

3.11、请求加解密

3.12、指定请求/回调线程

3.13、 Retrofit用户

4、原理剖析

4.1、工作流程

在RxHttp有4个重要的角色,分别是:

  • Param:RxHttp类中所有添加的参数/请求头/文件都交由它处理,它最终目的就是为了构建一个Request对象

  • HttpSender :它负责从Param对象中拿到Request对象,从而执行请求,最终返回Response对象

  • Parser:它负责将HttpSender返回的Response对象,解析成我们期望的实体类对象,也就是泛型T

  • RxHttp:它像一个管家,指挥前面3个角色做事情,当然,它也有自己的事情要做,比如:请求线程的调度,BaseUrl的处理、允许开发者自定义API等等

为此,我画了一个流程图,可以直观的了解到RxHttp的大致工作流程

我想应该很好理解,RxHttp要做的事情,就是把添加的参数/请求头等全部丢给Param处理,自己啥事也不敢;

随后将Param交给HttpSender,让它去执行请求,执行完毕,返回Response对象;

接着又将Response对象丢给Parser去做数据解析工作,并返回实体类对象T;

最后,将T通过回调传给开发者,到此,一个请求就处理完成。

4.2、Param

首先,附上一张Param类的继承关系图

下面将从上往下对上图中的类做个简单的介绍:

  • IHeaders:接口类,里面定义了一系列addHeader方法

  • IParam:接口类,里面定义了add(String,Object)、addAll(Map)等方法,

  • IRequest:接口类,里面定义了一系列getXxx方法,通过这些方法最终构建一个Request对象

  • Param:接口类,是一个空接口,继承了前面3个接口,里面有一系列静态方法可以获取到Param的具体实现类

  • AbstractParam:Param接口的抽象实现类,实现了Param接口的所有方法

  • IFile:接口类,里面定义了一系列addFile方法

  • IUploadLengthLimit:接口类,里面就定义了一个checkLength()方法,用于限制文件上传大小

  • NoBodyParam:Param的具体实现类,Get、Head请求就是通过该类去实现的

  • JsonParam:Param的具体实现类,调用RxHttp.xxxJson(String)请求方法时,内部就是通过该类去实现的

  • JsonArrayParam:Param的具体实现类,调用RxHttp.xxxJsonArray(String)请求方法时,内部就是通过该类去实现的

  • FormParam:Param的具体实现类,同时又实现了IFile、IUploadLengthLimit两个接口,调用RxHttp.xxxForm(String)请求方法时,内部就是通过该类去实现的

4.3、HttpSender

HttpSender可以把它理解为请求发送者,里面声明OkHttpClient对象和一系列静态方法,我们来简单看下:

public final class HttpSender {private static OkHttpClient mOkHttpClient; //只能初始化一次,第二次将抛出异常//处理化OkHttpClient对象public static void init(OkHttpClient okHttpClient) {if (mOkHttpClient != null)throw new IllegalArgumentException("OkHttpClient can only be initialized once");mOkHttpClient = okHttpClient;}//通过Param对象同步执行一个请求public static Response execute(@NonNull Param param) throws IOException {return newCall(param).execute();}static Call newCall(Param param) throws IOException {return newCall(getOkHttpClient(), param);}//所有的请求,最终都会调此方法拿到Call对象,然后执行请求static Call newCall(OkHttpClient client, Param param) throws IOException {param = RxHttpPlugins.onParamAssembly(param);if (param instanceof IUploadLengthLimit) {((IUploadLengthLimit) param).checkLength();}Request request = param.buildRequest();  //通过Param拿到Request对象LogUtil.log(request);return client.newCall(request);}//省略了部分方法
}

这里我们重点看下newCall(OkHttpClient, Param)方法,该方法第一行就是为Param添加公共参数;然后判断Param有没有实现IUploadLengthLimit接口,有的话,检查文件上传大小,超出大小,则抛出IO异常;接着就是通过Param拿到Request对象;最后拿到Call对象,就可以发送一个请求。

4.4、Parser

先看下Parser继承结构图

这里对上图中的类做个简单的介绍

  • Parser:接口类,里面定义了一个T onParse(Response)方法,输入Response对象,输出实体类对象T

  • AbstractParser:抽象类,里面没有任何具体实现,主要作用是在构造方法内获取泛型类型

  • SimpleParser:是一个万能的解析器,可以解析任意数据结构,RxHttp内置的大部分asXxx方法,内部就是通过该解析器实现的

  • ListParser:是一个列表解析器,可以解析任意列表数据,内置asList(Class<T>)方法,就是通过该解析器实现的

  • MapParser:是一个Map解析器,可以解析任意Map数据类型,内置的asMap系列方法,就是通过该解析器实现的

  • BitmapParser:是一个Bitmap解析器,通过该解析器可以获得一个Bitmap对象,asBitmap()方法内部就是通过该解析器实现的

  • DownloadParser:文件下载解析器,用于文件下载,内置的一系列asDownload方法就是通过该解析器实现的

5、扩展

在这教大家一个小技巧,由于使用RxHttp发送请求都遵循请求三部曲,故我们可以在android studio 设置代码模版,如下

如图设置好后,写代码时,输入rp,就会自动生成模版,如下:

6、小结

到这,RxHttp常用功能介绍完毕,你会发现,一切都是那么的美好,无论你是get、post、加密请求、自定义解析器,还是文件上传/下载/进度监听等等,皆遵循请求三部曲。

程序员专栏 扫码关注填加客服 长按识别下方二维码进群近期精彩内容推荐:   裁员1100人:华为,还是对中年人下手了 程序员转行卖煎饼送外卖,称找回了自己 新手必会的 9 个 Python 技巧! 面对Redis持久化连环Call,你顶得住吗?在看点这里好文分享给更多人↓↓

RxHttp 让你眼前一亮的Http请求框架相关推荐

  1. RxHttp 一款让你眼前一亮的 Http 请求框架

    本文系 RxHttp作者不怕天黑 向本博客投稿,并授权在本站发表. 1.前言 RxHttp在今年4月份一经推出,就受到了广大Android 开发者的喜爱,截止本文发表在github上已有1100+st ...

  2. 为何看完这篇RxHttp Http请求框架会觉得如此销魂,全文干货建议收藏!

    前言 RxHttp相较于retrofit,功能上,两者均能实现,并无多大差异,更多的差异体现功能的使用上,也就是易用性,如对文件上传/下载/进度监听的操作上,RxHttp用及简的API,可以说碾压re ...

  3. android post请求_Vue 网络请求框架 axios 使用教程

    点击上方"代码集中营",设为星标 优秀文章,第一时间送达! 前期回顾 1. Vue 学习入门指南 2. Vue 入门环境搭建 3. Visual Studio Code 使用指南 ...

  4. 如何独立开发一个网络请求框架

    (原创出处为本博客:http://www.cnblogs.com/linguanh/) 目录:   前言 准备工作  开发模式 开发原则 线程 高并发 TCP/UDP 本类介绍  开发选择 功能列表 ...

  5. android网络请求框架_2020,最新APP重构:网络请求框架

    在现在的app,网络请求是一个很重要的部分,app中很多部分都有或多或少的网络请求,所以在一个项目重构时,我会选择网络请求框架作为我重构的起点.在这篇文章中我所提出的架构,并不是所谓的 最好 的网络请 ...

  6. HttpSender OkHttp+RxJava超好用、功能超级强大的Http请求框架

    HttpSender HttpSender 是对OkHttp二次封装,并与RxJava做到了无缝连接,支持任意Http请求方式,如:Get.Post.Head.Put等:也支持任意数据解析方法,如:J ...

  7. App 组件化/模块化之路——如何封装网络请求框架

    App 组件化/模块化之路--如何封装网络请求框架 在 App 开发中网络请求是每个开发者必备的开发库,也出现了许多优秀开源的网络请求库.例如 okhttp retrofit android-asyn ...

  8. 一款基于RxJava2+Retrofit2实现简单易用的网络请求框架

    本库是一款基于RxJava2+Retrofit2实现简单易用的网络请求框架,结合android平台特性的网络封装库,采用api链式调用一点到底,集成cookie管理,多种缓存模式,极简https配置, ...

  9. Android Http请求框架二:xUtils 框架网络请求

    一:对Http不了解的请看 Android Http请求框架一:Get 和 Post 请求 二.正文 1.xUtils 下载地址 github 下载地址  : https://github.com/w ...

最新文章

  1. oracle回收ddl权限,查看oracle用户所有权限,并获取授权的DDL-Oracle
  2. udf、utaf、udtf进出数量规律
  3. DEV控件中GridView中的复选框与CheckBox实现联动的全选功能
  4. 根本不存在 DIV + CSS 布局这回事
  5. (86)FPGA同步复位与异步复位-面试必问(十)(第18天)
  6. 100个学生种树c语言,种树的作文100字
  7. webpack最佳入门实践系列(08)
  8. hadoop文件的序列化
  9. Python之random.seed()用法
  10. python 会议室预约系统解决方案_会议预约管理系统解决方案
  11. 计算机科学与技术基础与核心,浅谈计算机科学与技术专业核心课程教学
  12. 中秋之际献上【中秋快乐】藏头诗
  13. 计算机老师写对联给新人,写给教师结婚幽默对联【值得收藏】
  14. android源码编译烧鸡,板栗烧鸡的做法图文
  15. sphinx使用笔记
  16. 统计基础(九)多元回归模型
  17. 《三国演义》人物出场统计
  18. 区块链架构--fabric基本介绍
  19. VMware虚拟机银河麒麟服务器系统与物理windows主机网络设置相互ping通处理方案之桥接
  20. 关于康托展开和逆康托展开详解,及python代码

热门文章

  1. 中兴新支点国产操作系统半年新增用户10万+,将发布自主内核
  2. 中兴视觉大数据投资70亿元在武汉建设中兴智能光谷产业基地
  3. 开机流程,模块管理与loader0.0
  4. c语言数据结构校园导游系统,校园导游系统课程设计报告
  5. Java带图片的excel数据导入
  6. 浙江大学计算机科学与技术在哪个校区,浙江大学计算机科学与技术学院在哪个校区······...
  7. 浙江大学计算机预推免经历
  8. 趁着课余时间学点Python(十)面向对象的理解(前奏)
  9. 【升级U8+】升级U8错误:数据库中已存在名为 ‘VoucherCustomerInfo‘ 的对象
  10. 学校计算机教室报损登记本,学校设施设备管理制度规定