内容简介:RxHttp是基于OkHttp的二次封装,并于RxJava做到无缝衔接,一条链就能发送一个完整的请求。主要功能如下:此步骤是非必须的,不初始化或者传入
简介
RxHttp是基于OkHttp的二次封装,并于RxJava做到无缝衔接,一条链就能发送一个完整的请求。主要功能如下:
- 支持Get、Post、Put、Delete等任意请求方式,可自定义请求方式
- 支持Json、DOM等任意数据解析方式,可自定义数据解析器
- 支持文件下载/上传,及进度的监听,并且支持断点下载
- 支持在Activity/Fragment的任意生命周期方法,自动关闭未完成的请求
- 支持添加公共参数/头部信息,且可动态更改baseUrl
- 支持请求串行和并行
gradle依赖
implementation 'com.rxjava.rxhttp:rxhttp:1.0.3' //注解处理器,生成RxHttp类,即可一条链发送请求 annotationProcessor 'com.rxjava.rxhttp:rxhttp-compiler:1.0.3' //管理RxJava及生命周期,Activity/Fragment 销毁,自动关闭未完成的请求 implementation 'com.rxjava.rxlife:rxlife:1.0.4'
初始化
//设置debug模式,此模式下有日志打印 HttpSender.setDebug(boolean debug) //非必须,只能初始化一次,第二次将抛出异常 HttpSender.init(OkHttpClient okHttpClient) //或者,调试模式下会有日志输出 HttpSender.init(OkHttpClient okHttpClient, boolean debug)
此步骤是非必须的,不初始化或者传入 null
即代表使用默认OkHttpClient对象。
疑问:标题不是说好的是RxHttp,这么用HttpSender做一些初始化呢?这里先卖一个关子,后面会解答
添加公共参数/头部及重新设置url
相信大多数开发者在开发中,都遇到要为Http请求添加公共参数/请求头,甚至要为不同类型的请求添加不同的公共参数/请求头,为此,RxHttp为大家提供了一个静态接口回调,如下,每发起一次请求,此接口就会被回调一次,并且此回调在子线程进行(在请求执行线程回调)
HttpSender.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 = Param.get("http://...") .add("key", "value"); Disposable disposable = HttpSender.from(param) .subscribe(s -> { //这里的s为String类型,即Http请求的返回结果 //成功回调 }, throwable -> { //失败回调 });
疑问:说好的一条链发送请求呢?别着急,还没到放大招的时候
到这,我可以告诉大家, Param
承担的是一个请求体的一个角色,我们通过 Param
可以确定请求方式(如:Get、Post、Put、Delete等请求方式)、添加请求参数、添加请求头、添加File对象等;然后通过HttpSender,传入 Param
对象,将请求发送出去。
HttpSender
到这,有人又有疑问,前面初始化、设置公共参数都用到了HttpSender,这里发送请求又用到了HttpSender ,那么它又是承担怎么样的一个角色呢?看名字,我们可以理解为它就是一个请求发送者,通过一个 from
操作符,传入一个 Param
对象,然后返回一个 RxJava
的 Observable
对象,此时,我们就可以使用RxJava强大的操作符去处理相关的逻辑(这就是简介说的,做到了与RxJava的无缝链接),在这,我们只是使用了 subscribe
操作符去订阅观察者。
RxHttp
现在,我们正式放大招,标题说好的一条链发送请求,既然吹牛了,就要去实现它。拿上面的例子,看看我们如何一条链实现,上代码
RxHttp.get("http://...") .add("key", "value") .from() .subscribe(s -> { //这里的s为String类型,即Http请求的返回结果 //成功回调 }, throwable -> { //失败回调 });
我们的主角 RxHttp 终于登场了,可以看到使用RxHttp类我们就实现了一条链完成请求的发送,那它又是承担一个什么角色呢?我们暂时可以理解为 RxHttp=Param+HttpSender
,并且还有自己特殊的使命。至于什么使用,后面会讲解。
我们现在来解疑惑,为什么我们的库叫 RxHttp
,但是初始化、设置公共参数等却用HttpSender?因为 RxHttp
这个类不在RxHttp库中,它是通过注解处理器生成的类。前面我们看到gradle依赖时,使用了
annotationProcessor 'com.rxjava.rxhttp:rxhttp-compiler:1.0.2'
该注解处理器的目的就是在项目中生成RxHttp类,那为何不直接把它写到库里面去呢?前面讲过,因为它有自己的使命,而这个使命,就是我们可以通过注解,在RxHttp中生成自定义的api,我们来看看如何使用注解。
动态设置baseUrl
现实开发中,大部人开发者都会将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") .from() .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") .from() .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
Post
RxHttp.postForm("http://...") .add("key", "value") .from() .subscribe(s -> { //这里的s为String类型,即Http请求的返回结果 //成功回调 }, throwable -> { //失败回调 });
可以看到,跟上面的Get请求只有一点不同,Get是 RxHttp.get
,而Post是 RxHttp.postForm
,除此之外,没有任何区别,我们在看来来,RxHttp都有哪些静态方法供我们选择请求方式
可以看到,默认提供了10个静态方法供我们选择具体的请求方式,有Get、Post、Put等,而Post等又分为postForm和postJson,这个好理解,前者是发送表单形式的post请求,后者是发送json字符串的post请求。
现实中,这些默认的请求方式显然不能满足我们的需求,如:我要发送加密的post请求,这个时候该怎么办呢?此时就需要我们自定义请求方式。自定义请求方式请查看 RxHttp 一条链发送请求之强大的Param类(三)
Activity 销毁,自动关闭未完成的请求
上面的案例中,在Activity/Fragment销毁时,如果请求还未完成,就会造成Activity/Fragment 无法回收,导致内存泄漏。这是非常严重的问题,那么 RxHttp
是如何解决的呢?此时,就要引入我自己写的另一个库 RxLife ,直接看看如何使用
RxHttp.postForm("http://...") .add("key", "value") .from() .as(RxLife.as(this)) //订阅观察者前,加上这句话即可 .subscribe(s -> { //成功回调 }, throwable -> { //失败回调 }); //或者 RxHttp.postForm("http://...") .add("key", "value") .from() .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")) .from() //from操作符,是异步操作 .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") .download(destPath) //注意这里使用download操作符,并传入本地路径 .as(RxLife.asOnMain(this)) //感知生命周期,并在主线程回调 .subscribe(s -> { //下载成功,回调文件下载路径 }, throwable -> { //下载失败 });
下载跟普通请求不同的是,下载使用的是 download
操作符,其它都一样。
文件下载进度监听
//文件存储路径 String destPath = getExternalCacheDir() + "/" + System.currentTimeMillis() + ".apk"; RxHttp.get("http://update.9158.com/miaolive/Miaolive.apk") .downloadProgress(destPath) //注:如果需要监听下载进度,使用downloadProgress操作符 .observeOn(AndroidSchedulers.mainThread()) .doOnNext(progress -> { //下载进度回调,0-100,仅在进度有更新时才会回调,最多回调101次,最后一次回调文件存储路径 int currentProgress = progress.getProgress(); //当前进度 0-100 long currentSize = progress.getCurrentSize(); //当前已下载的字节大小 long totalSize = progress.getTotalSize(); //要下载的总字节大小 String filePath = progress.getResult(); //文件存储路径,最后一次回调才有内容 }) .filter(Progress::isCompleted)//下载完成,才继续往下走 .map(Progress::getResult) //到这,说明下载完成,返回下载目标路径 .as(RxLife.as(this)) //感知生命周期 .subscribe(s -> {//s为String类型,这里为文件存储路径 //下载完成,处理相关逻辑 }, throwable -> { //下载失败,处理相关逻辑 });
下载进度的监听我们稍微看一下 ,首先一点,下载使用 download
操作符,而下载进度监听使用 downloadProgress
操作符,随后,我们使用了 doOnNext
操作符处理进度回调,注意这里是仅当有进度更新时,才会回调,其中的 progress
变量是一个 Progress
类型的对象,我们贴上源码:
public class Progress<T> { private int progress; //当前进度 0-100 private long currentSize;//当前已完成的字节大小 private long totalSize; //总字节大小 private T mResult; //http返回结果,上传/下载完成时调用 //省略get/set方法 }
由于进度回调会执行101次(上面注释有解释),而最下面观察者其实是不需要关心这么多事件的,只需要关心最后下载完成的事件,所以使用了 filter
操作符过滤事件,只要还未下载完成,就将事件过滤调,不让往下走。最终下载完成后,拿到本地下载路径。
文件上传进度监听
RxHttp.postForm("http://www.......") //发送Form表单形式的Post请求 .add("file1", new File("xxx/1.png")) .add("file2", new File("xxx/2.png")) .add("key1", "value1")//添加参数,非必须 .add("key2", "value2")//添加参数,非必须 .addHeader("versionCode", "100") //添加请求头,非必须 .uploadProgress() //注:如果需要监听上传进度,使用uploadProgress操作符 .observeOn(AndroidSchedulers.mainThread()) //主线程回调 .doOnNext(progress -> { //上传进度回调,0-100,仅在进度有更新时才会回调,最多回调101次,最后一次回调Http执行结果 int currentProgress = progress.getProgress(); //当前进度 0-100 long currentSize = progress.getCurrentSize(); //当前已上传的字节大小 long totalSize = progress.getTotalSize(); //要上传的总字节大小 String result = progress.getResult(); //Http执行结果,最后一次回调才有内容 }) .filter(Progress::isCompleted)//过滤事件,上传完成,才继续往下走 .map(Progress::getResult) //到这,说明上传完成,拿到Http返回结果并继续往下走 .as(RxLife.as(this)) //感知生命周期 .subscribe(s -> { //s为String类型,由SimpleParser类里面的泛型决定的 //上传成功,处理相关逻辑 }, throwable -> { //上传失败,处理相关逻辑 });
上传进度监听使用 downloadProgress
操作符,剩下的操作跟下载进度监听的操作都一样,通过 doOnNext
监听上传进度,然后过滤事件,最终拿到Http的返回结果。
数据解析器Parser
在上面的案例中,观察者拿到数据类型都是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)") .fromSimpleParser(Response.class) //这里返回Observable<Response> 对象 .as(RxLife.asOnMain(this)) //感知生命周期,并在主线程回调 .subscribe(response -> { //成功回调 }, throwable -> { //失败回调 });
可以看到,这里我们没有用 from
操作符,而是用了 fromSimpleParser
操作符,并且传入 Response.class
,最后观察者拿到的response变量就是Response类型的对象。怎么样,是不是很简单。RxHttp为我们提供了一系列的fromXXX方法,我们来看一下:
我们可以看到,一些基本类型的封装对象RxHttp都为我们封装好了,还有一个 fromListParser
方法,此方法是用来解析集合对象的,一些常见的数据结构,RxHttp都为我们考虑到了,并封装好了,然后,一些不常见的数据呢?眼尖的你也许发现了,上图中还有一个 <T> Observable<T> from(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字段判断是否添加头部信息 .fromSimpleParser(String.class) //这里返回Observable<T> 对象 fromXXX都是异步操作符 //感知生命周期,并在主线程回调,当Activity/Fragment销毁时,自动关闭未完成的请求 .as(RxLife.asOnMain(this)) .subscribe(s -> { //订阅观察者 //成功回调 }, throwable -> { //失败回调 });
小结
到这, RxHttp
的基本用法我们就讲解完毕了,可以看到,使用 RxHttp
类一条链就能完成一个完整的Http请求,简单一点,就是请求三部曲:
- 首先,确定请求方式并添加相关参数
- 然后,确定解析器,指定要解析成的类型
- 最后,订阅观察者,开始发送请求
以上所有的案例都离不开这3个步骤。最后,你会发现, RxHttp
除了提供的一系列强大的功能外,在写法上,不管什么请求,都极其的相似,只要通过 RxHttp
类,就能一条链,完成所有的请求,极大的降低了学习成本。
注:
要想在项目中生成RxHttp类,至少需要使用一次注解类,否则检测不到注解,就无法生成。
如果你觉得 RxHttp + RxLife 好用,请记得给我star
如果有好的idea,请留言或者联系我。
更过详情请查看RxHttp系列其它文章
以上所述就是小编给大家介绍的《RxHttp 一条链发送请求,新一代Http请求神器(一)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
实现领域驱动设计
Vaughn Vernon / 滕云 / 电子工业出版社 / 2014-3 / 99.00元
领域驱动设计(DDD)是教我们如何做好软件的,同时也是教我们如何更好地使用面向对象技术的。它为我们提供了设计软件的全新视角,同时也给开发者留下了一大难题:如何将领域驱动设计付诸实践?Vaughn Vernon 的这本《实现领域驱动设计》为我们给出了全面的解答。 《实现领域驱动设计》分别从战略和战术层面详尽地讨论了如何实现DDD,其中包含了大量的最佳实践、设计准则和对一些问题的折中性讨论。《实......一起来看看 《实现领域驱动设计》 这本书的介绍吧!