本文已受权「玉刚说」微信公众号独家原创发布php
RxHttp是基于OkHttp的二次封装,并与RxJava作到无缝衔接,一条链就能发送一个完整的请求。主要功能以下:java
1. 支持Json、DOM等任意数据解析方式,可自定义数据解析器android
2. 支持Get、Post、Put、Delete等任意请求方式,可自定义请求方式git
3. 支持在Activity/Fragment/View/ViewModel/任意类中,自动关闭请求github
4. 支持统一加解密,且可对单个请求设置是否加解密json
5. 支持添加公共参数/头部,且可对单个请求设置是否添加公共参数/头部api
6. 史上最优雅的实现文件上传/下载及进度的监听,且支持断点下载缓存
7. 史上最优雅的对错误统一处理,且不打破Lambda表达式微信
8. 史上最优雅的处理多个BaseUrl及动态BaseUrl数据结构
9. 30秒便可上手,学习成本极低
gradle依赖
implementation 'com.rxjava.rxhttp:rxhttp:1.1.6'
//注解处理器,生成RxHttp类,便可一条链发送请求
annotationProcessor 'com.rxjava.rxhttp:rxhttp-compiler:1.1.6'
//管理RxJava及生命周期,Activity/Fragment 销毁,自动关闭未完成的请求
implementation 'com.rxjava.rxlife:rxlife:1.0.9'
复制代码
//设置debug模式,此模式下有日志打印
RxHttp.setDebug(boolean debug)
//非必须,只能初始化一次,第二次将抛出异常
RxHttp.init(OkHttpClient okHttpClient)
//或者,调试模式下会有日志输出
RxHttp.init(OkHttpClient okHttpClient, boolean debug)
复制代码
此步骤是非必须的,不初始化或者传入null
即表明使用默认OkHttpClient对象。
疑问:标题不是说好的是RxHttp,这么用HttpSender作一些初始化呢?这里先卖一个关子,后面会解答
相信大多数开发者在开发中,都遇到要为Http请求添加公共参数/请求头,甚至要为不一样类型的请求添加不一样的公共参数/请求头,为此,RxHttp为你们提供了一个静态接口回调,以下,每发起一次请求,此接口就会被回调一次,而且此回调在子线程进行(在请求执行线程回调)
RxHttp.setOnParamAssembly(new Function() {
@Override
public Param apply(Param p) {
if (p instanceof GetRequest) {//根据不一样请求添加不一样参数
} else if (p instanceof PostRequest) {
} else if (p instanceof PutRequest) {
} else if (p instanceof DeleteRequest) {
}
//能够经过 p.getSimpleUrl() 拿到url更改后,从新设置
//p.setUrl("");
return p.add("versionName", "1.0.0")//添加公共参数
.addHeader("deviceType", "android"); //添加公共请求头
}
});
复制代码
而后有些请求咱们不但愿添加公共参数/请求头,RxHttp又改如何实现呢?很简单,发起请求前,设置不添加公共参数,以下:
Param param = Param.get("http://...")
//设置是否对Param对象修饰,便是否添加公共参数,默认为true
.setAssemblyEnabled(false); //设为false,就不会回调上面的静态接口
复制代码
到这,也许大家会有疑问,Param
是什么东东,下面就为你们讲解。
首先,咱们来看看如何发送一个请求
Param param = Param.get("http://...")
.add("key", "value");
HttpSender.from(param)
.subscribe(s -> { //这里的s为String类型,即Http请求的返回结果
//成功回调
}, throwable -> {
//失败回调
});
复制代码
疑问:说好的一条链发送请求呢?别着急,还没到放大招的时候
到这,我能够告诉你们,Param
承担的是一个请求体的一个角色,咱们经过Param
能够肯定请求方式(如:Get、Post、Put、Delete等请求方式)、添加请求参数、添加请求头、添加File对象等;而后经过HttpSender,传入Param
对象,将请求发送出去。
到这,有人又有疑问,前面初始化、设置公共参数都用到了HttpSender,这里发送请求又用到了HttpSender ,那么它又是承担怎么样的一个角色呢?看名字,咱们能够理解为它就是一个请求发送者,经过一个from
操做符,传入一个Param
对象,而后返回一个RxJava
的Observable
对象,此时,咱们就可使用RxJava强大的操做符去处理相关的逻辑(这就是简介说的,作到了与RxJava的无缝连接),在这,咱们只是使用了subscribe
操做符去订阅观察者。
如今,咱们正式放大招,标题说好的一条链发送请求,既然吹牛了,就要去实现它。拿上面的例子,看看咱们如何一条链实现,上代码
RxHttp.get("http://...")
.add("key", "value")
.asString()
.subscribe(s -> { //这里的s为String类型,即Http请求的返回结果
//成功回调
}, throwable -> {
//失败回调
});
复制代码
咱们的主角RxHttp终于登场了,能够看到使用RxHttp类咱们就实现了一条链完成请求的发送,那它又是承担一个什么角色呢?咱们暂时能够理解为RxHttp=Param+HttpSender
,而且还有本身特殊的使命。至于什么使用,后面会讲解。
咱们如今来解疑惑,为何咱们的库叫RxHttp
,可是初始化、设置公共参数等却用HttpSender?由于RxHttp
这个类不在RxHttp库中,它是经过注解处理器生成的类。前面咱们看到gradle依赖时,使用了
annotationProcessor 'com.rxjava.rxhttp:rxhttp-compiler:1.1.6'
复制代码
该注解处理器的目的就是在项目中生成RxHttp类,那为什么不直接把它写到库里面去呢?前面讲过,由于它有本身的使命,而这个使命,就是咱们能够经过注解,在RxHttp中生成自定义的api,咱们来看看如何使用注解。
现实开发中,大部人开发者都会将baseUrl 单独抽取出来,RxHttp也考虑到了这一点,RxHttp经过@DefaultDomain
注解来配置baseUrl,看代码
public class Url {
@DefaultDomain() //设置为默认域名
public static String baseUrl = "http://ip.taobao.com/";
}
复制代码
rebuild一下项目,此时咱们发送请求就能够直接传入path路径,以下:
RxHttp.get("/service/getIpInfo.php")
.add("key", "value")
.asString()
.subscribe(s -> { //这里的s为String类型,即Http请求的返回结果
//成功回调
}, throwable -> {
//失败回调
});
复制代码
RxHttp在发送请求前,会对url作判断,若是没有域名,就会自定加上默认的域名,也就是baseUrl。而后,若是咱们不想使用默认的域名呢?RxHttp也考虑到来,提供了一个@Domain
注解,咱们再来看看用法:
public class Url {
@Domain(name = "Update9158") //设置非默认域名,name 可不传,不传默认为变量的名称
public static String update = "http://update.9158.com";
@DefaultDomain() //设置为默认域名
public static String baseUrl = "http://ip.taobao.com/";
}
复制代码
此时再rebuild一下项目,就会在RxHttp类中生成一个setDomainToUpdate9158IfAbsent()
方法,其中的Update9158
字符就是name
指定的名字,而后发请求就能够这样:
RxHttp.get("/service/getIpInfo.php")
.setDomainToUpdate9158IfAbsent()
.add("key", "value")
.asString()
.subscribe(s -> { //这里的s为String类型,即Http请求的返回结果
//成功回调
}, throwable -> {
//失败回调
});
复制代码
此时,RxHttp检测到url已经配置了域名,就不会再去使用默认的域名。一样的,setDomainToUpdate9158IfAbsent
也会检测url 有没有配置域名,若是配置了,也不会使用咱们指定的域名。
注意:
@Domain注解能够在多个地方使用,而@DefaultDomain()只能在一个地方使用,不然编译不经过,很好理解,默认域名只可能有一个。两个注解都要使用在public static
修饰的String类型变量上,对final
关键字没有要求,可写可不写,这就代表,baseUrl 能够动态更改,RxHttp始终会拿到的最新的baseUrl 。怎么样,是否是很nice!!
更多注解使用请查看RxHttp 一条链发送请求之注解处理器 Generated API(四)
接下来,咱们来看看,如何发送Post请求、如何在Activity/Fragment销毁时,自动关闭为完成的请求、如何上传/下载文件及进度的监听、如何把Http返回的结果自动解析成咱们想要的对象。
注:
如下讲解均使用RxHttp
RxHttp.postForm("http://...")
.add("key", "value")
.asString()
.subscribe(s -> { //这里的s为String类型,即Http请求的返回结果
//成功回调
}, throwable -> {
//失败回调
});
复制代码
能够看到,跟上面的Get请求只有一点不一样,Get是RxHttp.get
,而Post是RxHttp.postForm
,除此以外,没有任何区别,咱们在看来来,RxHttp都有哪些静态方法供咱们选择请求方式
现实中,这些默认的请求方式显然不能知足咱们的需求,如:我要发送加密的post请求,这个时候该怎么办呢?此时就须要咱们自定义请求方式。自定义请求方式请查看RxHttp 一条链发送请求之强大的Param类(三)
上面的案例中,在Activity/Fragment销毁时,若是请求还未完成,就会形成Activity/Fragment 没法回收,致使内存泄漏。这是很是严重的问题,那么RxHttp
是如何解决的呢?此时,就要引入我本身写的另外一个库RxLife,直接看看如何使用
RxHttp.postForm("http://...")
.add("key", "value")
.asString()
.as(RxLife.as(this)) //订阅观察者前,加上这句话便可
.subscribe(s -> {
//成功回调
}, throwable -> {
//失败回调
});
//或者
RxHttp.postForm("http://...")
.add("key", "value")
.asString()
.as(RxLife.asOnMain(this)) //asOnMain 能够在主线程回调观察者
.subscribe(s -> {
//成功回调
}, throwable -> {
//失败回调
});
复制代码
这里的this
为LifecycleOwner
对象,它是一个接口,这里咱们传入的是Activity,由于Activity实现了LifecycleOwner
接口。当Activity/Fragment销毁时,会将RxJava的管道中断,管道中断时,又会将未完成的请求自动关闭。 对RxLife
不了解的同窗请查看Android RxLife 一款轻量级别的RxJava生命周期管理库 (一),这里不详细讲解。在下面的讲解中,咱们均会使用RxLife
使用RxHttp
,能够很优雅的实现文件上传/下载及进度的监听,如何优雅?直接上代码
文件上传
RxHttp.postForm("http://...") //发送Form表单形式的Post请求
.add("key", "value")
.add("file1", new File("xxx/1.png")) //添加file对象
.add("file2", new File("xxx/2.png"))
.asString() //asXXX操做符,是异步操做
.as(RxLife.asOnMain(this)) //感知生命周期,并在主线程回调
.subscribe(s -> {
//成功回调
}, throwable -> {
//失败回调
});
复制代码
能够看到,文件上传跟普通的post请求其实没啥区别,无非就是在post请求的基础上,调用add方法添加要上传的文件对象。
文件下载
//文件存储路径
String destPath = getExternalCacheDir() + "/" + System.currentTimeMillis() + ".apk";
RxHttp.get("http://update.9158.com/miaolive/Miaolive.apk")
.asDownload(destPath) //注意这里使用download操做符,并传入本地路径
.as(RxLife.asOnMain(this)) //感知生命周期,并在主线程回调
.subscribe(s -> {
//下载成功,回调文件下载路径
}, throwable -> {
//下载失败
});
复制代码
下载跟普通请求不一样的是,下载使用的是asDownload
操做符,其它都同样。
文件下载进度监听
//文件存储路径
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-100
long currentSize = progress.getCurrentSize(); //当前已下载的字节大小
long totalSize = progress.getTotalSize(); //要下载的总字节大小
}, AndroidSchedulers.mainThread())//指定主线程回调
.as(RxLife.as(this)) //感知生命周期
.subscribe(s -> {//s为String类型,这里为文件存储路径
//下载完成,处理相关逻辑
}, throwable -> {
//下载失败,处理相关逻辑
});
复制代码
文件上传进度监听
RxHttp.postForm("http://www.......") //发送Form表单形式的Post请求
.add("key1", "value1")//添加参数,非必须
.add("file1", new File("xxx/1.png"))
.asUpload(progress -> {
//上传进度回调,0-100,仅在进度有更新时才会回调,最多回调101次,最后一次回调Http执行结果
int currentProgress = progress.getProgress(); //当前进度 0-100
long currentSize = progress.getCurrentSize(); //当前已上传的字节大小
long totalSize = progress.getTotalSize(); //要上传的总字节大小
}, AndroidSchedulers.mainThread()) //指定主线程回调
.as(RxLife.as(this)) //感知生命周期
.subscribe(s -> { //s为String类型,由SimpleParser类里面的泛型决定的
//上传成功,处理相关逻辑
}, throwable -> {
//上传失败,处理相关逻辑
});
复制代码
上传进度监听使用asUpload(Consumer, Scheduler)
方法,剩下的操做跟下载进度监听的操做都同样
在上面的案例中,观察者拿到数据类型都是String类型,而后现实开发中,咱们常常须要对数据解析成咱们想要的对象,RxHttp
考虑到了这一点,如今咱们就来看看如何的到咱们想要的对象
咱们拿淘宝获取IP的接口做为测试接口http://ip.taobao.com/service/getIpInfo.php?ip=63.223.108.42
对应的数据结构以下
public class Response {
private int code;
private Address data;
//省略set、get方法
class Address {
//为简单起见,省略了部分字段
private String country; //国家
private String region; //地区
private String city; //城市
//省略set、get方法
}
}
复制代码
开始发送请求
RxHttp.get("http://ip.taobao.com/service/getIpInfo.php") //Get请求
.add("ip", "63.223.108.42")//添加参数
.addHeader("accept", "*/*") //添加请求头
.addHeader("connection", "Keep-Alive")
.addHeader("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)")
.asObject(Response.class) //这里返回Observable<Response> 对象
.as(RxLife.asOnMain(this)) //感知生命周期,并在主线程回调
.subscribe(response -> {
//成功回调
}, throwable -> {
//失败回调
});
复制代码
能够看到,这里咱们使用asObject
操做符,而且传入Response.class
,最后观察者拿到的response变量就是Response类型的对象。怎么样,是否是很简单。RxHttp为咱们提供了一系列的asXXX方法,咱们来看一下:
asList
方法,此方法是用来解析集合对象的,一些常见的数据结构,RxHttp都为咱们考虑到了,并封装好了,而后,一些不常见的数据呢?眼尖的你也许发现了,上图中还有一个
<T> Observable<T> asParser(Parser<T> parser)
方法,它容许咱们传入一个自定义的解析器,更多解析器的介绍,请查看
RxHttp 之强大的数据解析功能(二)
最后,附上RxHttp一些经常使用的用法,以下:
RxHttp.postForm("/service/getIpInfo.php") //发送Form表单形式的Post请求
.setDomainToUpdate9158IfAbsent() //手动设置域名,此方法是经过@Domain注解生成的
.tag("RxHttp.get") //为单个请求设置tag
.setUrl("http://...") //从新设置url
.setAssemblyEnabled(false) //设置是否添加公共参数,默认为true
.cacheControl(CacheControl.FORCE_NETWORK) //缓存控制
.setParam(Param.postForm("http://...")) //从新设置一个Param对象
.add(new HashMap<>()) //经过Map添加参数
.add("int", 1) //添加int类型参数
.add("float", 1.28838F) //添加float类型参数
.add("double", 1.28838) //添加double类型参数
.add("key1", "value1") //添加String类型参数
.add("key2", "value2", false) //根据最后的boolean字段判断是否添加参数
.add("file1", new File("xxx/1.png")) //添加文件对象
.addHeader("headerKey1", "headerValue1") //添加头部信息
.addHeader("headerKey2", "headerValue2", false)//根据最后的boolean字段判断是否添加头部信息
.asString() //这里返回Observable<T> 对象 asXXX都是异步操做符
//感知生命周期,并在主线程回调,当Activity/Fragment销毁时,自动关闭未完成的请求
.as(RxLife.asOnMain(this))
.subscribe(s -> { //订阅观察者
//成功回调
}, throwable -> {
//失败回调
});
复制代码
到这,RxHttp
的基本用法咱们就讲解完毕了,能够看到,使用RxHttp
类一条链就能完成一个完整的Http请求,简单一点,就是请求三部曲:
以上全部的案例都离不开这3个步骤。最后,你会发现,RxHttp
除了提供的一系列强大的功能外,在写法上,无论什么请求,都极其的类似,只要经过RxHttp
类,就能一条链,完成全部的请求,极大的下降了学习成本。
注:
要想在项目中生成RxHttp类,至少须要使用一次注解类,不然检测不到注解,就没法生成。
若是你以为RxHttp+RxLife好用,请记得给我star 若是有好的idea,请留言或者联系我。
更过详情请查看RxHttp系列其它文章