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

82,452 阅读33分钟

1、前言

RxHttp在19年4月份一经推出,就受到了广大Android 开发者的喜爱,截止目前(20年5月)在github上突破2000+star,为此,我自己也建个RxHttp&RxLife 的群(群号:378530627)目前群里也500+号人,里面有不少小伙伴提了很多有价值的创意,才使得RxHttp一直坚持走到了现在,在此,感谢大家的喜爱。

这期间,一直有人问我,retrofit不香吗?之前不知道该如何回答这个问题,现在我想说,香!!retrofit无疑是目前综合得分最高的选手,但它也有它的不足。

RxHttp相较于retrofit,功能上,两者均能实现,并无多大差异,更多的差异体现功能的使用上,也就是易用性,如对文件上传/下载/进度监听的操作上,RxHttp用及简的API,可以说碾压retrofit;另外在baseUrl、公共参数/请求头、请求加解密等功能上的易用性都要优于retrofit;然而这些,个人觉得都不算什么,个人觉得RxHttp最大的优势在于它近乎为0的上手成本、极简的API以及高扩展性,看完这篇文章,相信你会有同感。

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

更多功能请查看

RxHttp ,比Retrofit 更优雅的协程体验

RxHttp 完美适配Android 10/11 上传/下载/进度监听

RxHttp 全网Http缓存最优解

2、简介

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

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

2. 完美支持 Kotlin 协程

3. 史上最优雅的处理多个BaseUrl及动态BaseUrl

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

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

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

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

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

9. 支持全局加解密、添加公共参数及头部、网络缓存,均支持对某个请求单独设置

3、使用

3.1、准备工作

gradle依赖

  • OkHttp 3.14.x以上版本, 最低要求为API 21,如你想要兼容21以下,请依赖OkHttp 3.12.x,该版本最低要求 API 9

  • toObserableXxx方法内部是通过RxJava实现的,而RxHttp 2.2.0版本起,内部已剔除RxJava,如需使用,请自行依赖RxJava并告知RxHttp依赖的Rxjava版本

必须

jitpack添加到项目的build.gradle文件中,如下:

allprojects {
    repositories {
        maven { url "https://jitpack.io" }
    }
}

注:RxHttp 2.6.0版本起,已全面从JCenter迁移至jitpack

//使用kapt依赖rxhttp-compiler时必须
apply plugin: 'kotlin-kapt'

android {
    //必须,java 8或更高
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

dependencies {
    def rxhttp_version = '3.3.1'
    implementation 'com.squareup.okhttp3:okhttp:4.11.0' 
    implementation "com.github.liujingxing.rxhttp:rxhttp:$rxhttp_version"
    kapt "com.github.liujingxing.rxhttp:rxhttp-compiler:$rxhttp_version" //生成RxHttp类,纯Java项目,请使用annotationProcessor代替kapt
 }

可选

android {
    kapt {
        arguments {
            //依赖了RxJava时,rxhttp_rxjava参数为必须,传入RxJava版本号
            arg("rxhttp_rxjava", "3.1.6")  
            arg("rxhttp_package", "rxhttp")  //指定RxHttp类包名,非必须
        }
    }
    //如果项目未集成kotlin,通过javaCompileOptions方法传参,在defaultConfig标签下
    annotationProcessorOptions {
        arguments = [
            rxhttp_rxjava: '3.1.6',
            rxhttp_package: 'rxhttp'
        ]
    }
}
dependencies {    
    //rxjava2   (RxJava2/Rxjava3二选一,使用toObservableXxx方法时必须)
    implementation 'io.reactivex.rxjava2:rxjava:2.2.8'
    implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
    implementation 'com.github.liujingxing.rxlife:rxlife-rxjava2:2.2.2' //管理RxJava2生命周期,页面销毁,关闭请求

    //rxjava3
    implementation 'io.reactivex.rxjava3:rxjava:3.1.6'
    implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'
    implementation 'com.github.liujingxing.rxlife:rxlife-rxjava3:2.2.2' //管理RxJava3生命周期,页面销毁,关闭请求

    //非必须,根据自己需求选择 RxHttp默认内置了GsonConverter
    implementation "com.github.liujingxing.rxhttp:converter-serialization:$rxhttp_version"
    implementation "com.github.liujingxing.rxhttp:converter-fastjson:$rxhttp_version"
    implementation "com.github.liujingxing.rxhttp:converter-jackson:$rxhttp_version"
    implementation "com.github.liujingxing.rxhttp:converter-moshi:$rxhttp_version"
    implementation "com.github.liujingxing.rxhttp:converter-protobuf:$rxhttp_version"
    implementation "com.github.liujingxing.rxhttp:converter-simplexml:$rxhttp_version"
}

注:添加依赖后,需要rebuild一下项目,注解处理器才会生成RxHttp类; 另外kotlin用户,请使用kapt替代annotationProcessor

最后,Rebuild一下项目(必须的),就会自动生成RxHttp类

以上步骤后,还未生成RxHttp类,请查看RxHttp类没有生成,检查步骤

3.2、配置默认的BaseUrl

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

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

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

3.3、请求三部曲

image.png

注:RxHttp + 协程文档,请查看RxHttp ,比Retrofit 更优雅的协程体验

代码表示,发送一个最简单的请求,如下

RxHttp.get("http://...")  //第一步, 通过get、postXxx、putXxx等方法,确定请求类型         
    .toObserableString()  //第二步, 通过toObservableXxx系列方法,确定返回数据类型    
    .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")) //添加单个文件
    .addFiles("fileList", new ArrayList<>()) //添加多个文件
    ...

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

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

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

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

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

RxHttp.postForm("/service/...")  //发送post表单请求
    .add("key", "value")         //添加参数,可调用多次
    .toObservableList(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对象,则可以通过toObservableResponse(Class<T>)方法获取,如下:

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

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

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

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

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

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

RxHttp.postForm("/service/...")   //发送post表单请求
    .add("key", "value")          //添加参数,可调用多次
    .toObservableResponsePageList(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做其他的判断,执行不同业务逻辑,怎么办?

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

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

RxHttp.postForm("/service/...")   //发送post表单请求
    .add("key", "value")          //添加参数,可调用多次
    .toObservableResponse(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个toObserableXxx方法,可以说基本涵盖80%以上的业务场景,它们最终都是通过toObserable(Parser<T>)方法实现的,具体实现过程,这里先跳过,后续会详细讲解。

3.3.3、第三部曲:订阅回调

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

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

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

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

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

3.4、初始化

RxHttpPlugins.init(OkHttpClient)      //自定义OkHttpClient对象  
    .setDebug(boolean)                //是否开启调试模式,开启后,logcat过滤RxHttp,即可看到整个请求流程日志        
    .setCache(File, long, CacheMode)  //配置缓存目录,最大size及缓存模式              
    .setExcludeCacheKeys(String...)   //设置一些key,不参与cacheKey的组拼                          
    .setResultDecoder(Function)       //设置数据解密/解码器,非必须
    .setConverter(IConverter)         //设置全局的转换器,非必须       
    .setOnParamAssembly(Function);    //设置公共参数/请求头回调       

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

//Default OkHttpClient object in RxHttp                    
private static OkHttpClient getDefaultOkHttpClient() {        
    return new OkHttpClient.Builder().build();                         
}

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

3.5、公共参数/请求头

RxHttp支持为所有的请求添加公共参数/请求头,如下:

RxHttpPlugins.init(okHttpClient)
    .setOnParamAssembly(new Consumer() {
        @Override
        public void accept(Param p) { //此方法在UI线程执行,请勿执行耗时操作
            Method method = p.getMethod();
            if (method.isGet()) {     //可根据请求类型添加不同的参数
            } else if (method.isPost()) {
            }
            p.add("versionName", "1.0.0")//添加公共参数
                .addHeader("deviceType", "android"); //添加公共请求头
        }
    });

我们需要在RxHttp初始化的地方,通过setOnParamAssembly方法设置公共参数接口回调,此时每次发起请求,都会回调该接口。

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

RxHttp.get("/service/...")       //get请求 
    .setAssemblyEnabled(false)   //设置是否添加公共参数/头部,默认为true    
    .toObservableString()                  //返回字符串数据    
    .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") //非默认域名,并取别名为BaseUrlBaidu
    public static String baidu = "https://www.baidu.com/";
    
    @Domain(name = "BaseUrlGoogle") //非默认域名,并取别名为BaseUrlGoogle
    public 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/...")
    .toObservableString()  
    .subscribe();
    
//手动输入域名,此时 url = "https://www.mi.com/service/..."
RxHttp.get("https://www.mi.com/service/...")
    .toObservableString()  
    .subscribe();

//手动输入域名时,若再次指定域名,则无效
//此时 url = "https://www.mi.com/service/..."
RxHttp.get("https://www.mi.com/service/...")
    .setDomainToBaseUrlBaiduIfAbsent()  //此时指定Baidu域名无效
    .toObservableString()  
    .subscribe();
    
//使用谷歌域名,此时 url = "https://www.google.com/service/..."       
RxHttp.get("/service/...")
    .setDomainToBaseUrlGoogleIfAbsent() //指定使用Google域名
    .toObservableString()  
    .subscribe();

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

3.6.2、动态域名

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

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

3.7、关闭请求

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

3.7.1、自动关闭请求

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

//以下代码均在FragmentActivty/Fragment中调用

RxHttp.postForm("/service/...")
    .toObservableString()
    .to(RxLife.to(this)) //页面销毁、自动关闭请求
    .subscribe();
    //或者
RxHttp.postForm("/service/...")
    .toObservableString()
    .to(RxLife.toMain(this)) //页面销毁、自动关闭请求 并且在主线程回调观察者
    .subscribe();

//kotlin用户,请使用life或lifeOnMain方法,如下:
RxHttp.postForm("/service/...")
    .toObservableString()
    .life(this) //页面销毁、自动关闭请求
    .subscribe();
    //或者
RxHttp.postForm("/service/...")
    .toObservableString()
    .lifeOnMain(this) //页面销毁、自动关闭请求 并且在主线程回调观察者
    .subscribe();

上面的thisLifecycleOwner接口对象,我们的FragmentActivity/Fragment均实现了这个接口,所有我们在FragmentActivity/Fragment中可以直接传this。 对RxLife不了解的同学请查看RxLife 史上最优雅的管理RxJava生命周期,这里不详细讲解。

3.7.2、手动关闭请求

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

//订阅回调,可以拿到Disposable对象
Disposable disposable = RxHttp.get("/service/...")
    .toObservableString()  
    .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对象,添加多个文件     
    .toObservableString()                                      
    .subscribe(s -> {                              
        //上传成功                                     
    }, throwable -> {                              
        //上传失败                                     
    });                                            

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

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

可以看到,跟上传的代码相比,我们仅仅增加了onMainProgress方法,即可监听上传进度,另外,还可选择onProgress方法,改方法可指定回调线程

3.8.2、下载

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

  //文件存储路径
String destPath = getExternalCacheDir() + "/" + System.currentTimeMillis() + ".apk";
RxHttp.get("http://xxx/xxx.apk")
    .toDownloadObserable(destPath) 
    .subscribe(s -> {
        //下载成功,回调文件下载路径
    }, throwable -> {
        //下载失败
    });

如果你想监听下载进度,跟上传进度一样,调用onMainProgress方法即可,如下:

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

3.8.3、断点下载

断点下载相较于下载,仅需要在toDownloadObserable方法第二个参数传true即可,如下

String destPath = getExternalCacheDir() + "/" + "test.apk";
RxHttp.get("http://xxx/xxx.apk")
    .toDownloadObserable(destPath, true)
    .subscribe(s -> { //s为String类型
        //下载成功,处理相关逻辑
    }, throwable -> {
        //下载失败,处理相关逻辑
    });

当然,此时你想监听下载进度,一样的调用onMainProgress方法,如下:

String destPath = getExternalCacheDir() + "/" + "test.apk";
RxHttp.get("http://xxx/xxx.apk")
    .toDownloadObserable(destPath, true)
    .onMainProgress(progress -> {                                                     
        //下载进度回调,0-100,仅在进度有更新时才会回调,最多回调101次,最后一次回调文件存储路径                              
        int currentProgress = progress.getProgress(); //当前进度 0-100                 
        long currentSize = progress.getCurrentSize(); //当前已下载的字节大小           
        long totalSize = progress.getTotalSize();     //要下载的总字节大小             
    })  
    .subscribe(s -> { //s为String类型
        //下载成功,处理相关逻辑
    }, throwable -> {
        //下载失败,处理相关逻辑
    });

3.9、超时设置

3.9.1、设置全局超时

RxHttp内部默认的读、写、连接超时时间均为10s,如需修改,请自定义OkHttpClient对象,如下:

//设置读、写、连接超时时间为15s
OkHttpClient client = new OkHttpClient.Builder()
    .connectTimeout(15, TimeUnit.SECONDS)
    .readTimeout(15, TimeUnit.SECONDS)
    .writeTimeout(15, TimeUnit.SECONDS)
    .build();
RxHttp.init(client);

3.9.2、为单个请求设置超时

为单个请求设置超时,使用的是RxJava的timeout(long timeout, TimeUnit timeUnit)方法,如下:

RxHttp.get("/service/...")
    .toObservableString()
    .timeout(5, TimeUnit.SECONDS)//设置总超时时间为5s
    .to(RxLife.toMain(this))  //感知生命周期,并在主线程回调
    .subscribe(s -> {
        //成功回调
    }, (OnError) error -> {
        //失败回调
    });

注:这里设置的总超时时间要小于全局读、写、连接超时时间之和,否则无效

3.10、设置Converter

3.10.1、设置全局Converter

IConverter converter = FastJsonConverter.create();
RxHttpPlugins.init(OkHttpClient)
    .setConverter(converter)

3.10.2、为请求设置单独的Converter

首先需要在任意public类中通过@Converter注解声明Converter,如下:

public class RxHttpManager {
    @Converter(name = "XmlConverter") //指定Converter名称
    public static IConverter xmlConverter = XmlConverter.create();
}

然后,rebuild 一下项目,就在自动在RxHttp类中生成setXmlConverter()方法,随后就可以调用此方法为单个请求指定Converter,如下:

RxHttp.get("/service/...")
    .setXmlConverter()   //指定使用XmlConverter,不指定,则使用全局的Converter
    .toObservable(NewsDataXml.class)
    .to(RxLife.toMain(this))  //感知生命周期,并在主线程回调
    .subscribe(dataXml -> {
        //成功回调
    }, (OnError) error -> {
        //失败回调
    });

3.11、请求加解密

3.11.1、加密

请求加密,需要自定义Param,非常简单,详情请查看本文5.2章节----自定义Param

3.11.2、解密

有些时候,请求会返回一大串的密文,此时就需要将密文转化为明文,直接来看代码,如下:

//设置数据解密/解码器                                               
RxHttpPlugins.init(OkHttpClient)
    .setResultDecoder(new Function<String, String>() {
        //每次请求成功,都会回调这里,并传入请求返回的密文   
        @Override                                              
        public String apply(String s) throws Exception {   
            String plaintext = decode(s);   //将密文解密成明文,解密逻辑自己实现
            return plaintext;    //返回明文                                   
        }                                                      
    });                                                        

很简单,通过RxHttp.setResultDecoder(Function<String, String>)静态方法,传入一个接口对象,此接口会在每次请求成功的时候被回调,并传入请求返回的密文,只需要将密文解密后返回即可。

然而,有些请求是不需求解密的,此时就可以调用setDecoderEnabled(boolean)方法,并传入false即可,如下:

RxHttp.get("/service/...")
    .setDecoderEnabled(false)  //设置本次请求不需要解密,默认为true
    .toObservableString()
    .subscribe(s -> {
        //成功回调
    }, (OnError) error -> {
        //失败回调
    });

3.12、同步请求/指定回调线程

RxHttp默认在Io线程执行请求,也默认在Io线程回调,即默认在同一Io线程执行请求并回调,当然,我们也可以指定请求/回调所在线程。

3.12.1、同步请求

RxHttp默认在IO线程发起请求,即异步请求,如果需要同步请求,调用syncRequest方法即可,如下:

//指定请求所在线程,需要在第二部曲前任意位置调用,第二部曲后调用无效
RxHttp.get("/service/...")
    .toObservableString()  
    .syncRequest() //同步执行,
    .subscribe();

以上使用的皆是RxJava的线程调度器,不熟悉的请自行查阅相关资料,这里不做详细介绍。

3.12.2、指定回调所在线程

指定回调所在线程,依然使用RxJava的线程调度器,如下:

//指定回调所在线程,需要在第二部曲后调用
RxHttp.get("/service/...")
    .toObservableString()  
    .observeOn(AndroidSchedulers.mainThread()) //指定在主线程回调
    .subscribe(s -> { //s为String类型,主线程回调
        //成功回调
    }, throwable -> {
        //失败回调
    });

3.13、 Retrofit用户

时常会有童鞋问我,我是Retrofit用户,喜欢把接口写在一个类里,然后可以直接调用,RxHttp如何实现?其实,这个问题压根就不是问题,在介绍第二部曲的时候,我们知道,使用toObserableXxx方法后,就会返回Observable<T>对象,因此,我们就可以这样实现:

public class HttpWrapper {

    public static Observable<List<Student>> getStudent(int page) {
        return RxHttp.get("/service/...")
            .add("page", page)
            .toObservableList(Student.class);
    }
}

//随后在其它地方就可以直接调用
HttpWrapper.getStudent(1)
    .to(RxLife.toMain(this))  //主线程回调,并在页面销毁自动关闭请求(如果还未关闭的话)
    .subscribe(students -> { //学生列表
        //成功回调
    }, throwable -> {
        //失败回调
    });

很简单,封装的时候返回Observable<T>对象即可。

还有的同学问,我们获取列表的接口,页码是和url拼接在一起的,Retrofit可以通过占位符,那RxHttp又如何实现?简单,如下:

public class HttpWrapper {

    //单个占位符
    public static Observable<Student> getStudent(int page) {
        return RxHttp.get("/service/%d/...", page)  //使用标准的占位符协议
            .toObservable(Student.class);
    }
    
    //多个占位符
    public static Observable<Student> getStudent(int page, int count) {
        return RxHttp.get("/service/%1$d/%2$d/...", page, count)  //使用标准的占位符协议
            .toObservable(Student.class);
    }
}

这一点跟Retrofit不同,Retrofit是通过注解指定占位符的,而RxHttp是使用标准的占位符,我们只需要在url中声明占位符,随后在传入url的后面,带上对应的参数即可。

4、原理剖析

RxHttp使用到当下流行的注解处理器工具(Annotation Processing Tool,以下简称APT),像知名的EventbusButterKnifeDagger2Glide以及Jetpack库里非常好用Room数据库框架,都使用到了APT,它能够在编译时检索注解信息,通过Javapoet框架生成Java类、方法等相关代码(想生成Kotlin相关代码,使用kotlinpoet),并因此在运行时做到零性能损耗。

那么,APT给RxHttp带来了哪些优势?RxHttp又是如何使用APT的?继续往下看

说起APT,大家脑海里第一个想到的可能是解耦,没错,解耦是它的一大优势,其实它还有一个更大有优势,那就是根据配置,生成不同的代码逻辑;比如在RxHttp中,默认是不依赖RxJava的,但是如果你需要使用RxHttp + RxJava方式发送请求,就可以在annotationProcessorOptions标签中的rxhttp_rxjava参数来配置RxJava大版本,可传入RxJava2RxJava3,内部根据传入的RxJava版本,生成不同的代码,这样就可做到一套代码同时兼通RxJava2RxJava3,如果后续出了RxJava4RxJava5等新版本,一样可以兼容,而且非常简单。

同时兼容RxJava不同版本,这就是APT带给RxHttp的一大优势。

RxHttp是如何使用APT?在RxHttp中,一共定义了6个注解,如下:

  • @DefaultDomain:用它来指定默认的baseUrl,只能使用一次

  • @Domain:指定非默认的baseUrl,可使用多次

  • @Parser: 指定自定义的解析器,可使用多次,这个非常强大,可在解析器里写自己数据解析逻辑,并返回任意类型的数据,完美解决服务端返回的数据不规范问题

  • @Param:指定自定义的Param,可使用多次,发送统一加密请求时用到

  • @OkClient:为不同请求配置不同的OkHttpClient对象,可多次使用

  • @Converter:为不同请求配置不同的Converter对象,可多次使用

注:以上6个注解的具体使用方式,请查看RxHttp 注解使用

RxHttp的注解处理器是rxhttp-compiler,它首要任务就是生成RxHttp类,其次就是检索以上6个注解,生成对应的类及方法,这就使得,无论我们如何去自定义,写请求代码时,始终遵循请求三部曲,如我们要发送统一加密的请求,就可以直接使用@Param注解生成的方法,如下:

//发送加密的post表单请求,方法名可通过@Param注解随意指定
val student = RxHttp.postEncryptForm("/service/...")
    .add("key", "value")
    .toAwait<Student>()
    .await()

其它5个注解带来的优势就不一一介绍了,总之就是另一大优势,解耦,使得任意请求,皆遵循请求三部曲

RxHttp工作流程

接下来,讲讲RxHttp的工作流程,有5个重要的角色,分别是:

  • RxHttp:这是最重要的一个角色,所以请求的唯一入口,内部持有一个Param对象,它的职责是,请求参数/请求头/BaseUrl的处理,请求线程的调度,提供注解生成的方法等等,最终的使命就是通过Param构建一个okhttp3.Request对象,随后在构建一个okhttp3.Call对象,并把Call对象丢给ObservableAwait,然后由ObservableAwait真正的去执行请求

  • Param:它的职责是处理请求参数/请求头/url等一切用来构建okhttp3.Request需要的东西,最终使命就是构建okhttp3.Request对象,它被RxHttp类所持有,RxHttp把构建okhttp3.Request对象所需要的东西,都交给Param去实现的

  • Awiat:结合协程发请求时,真正执行网络请求的对象,具体实现类为AwaitImpl,它内部持有Parser对象,请求返回后,将okhttp3.Response丢给Parser去解析,并返回解析后的对象

  • Observable:结合RxJava发送请求时,真正执行网络请求的对象,具体实现类ObservableCall内部持有Parser对象,具体的解析工作都交给Parser

  • Parser:负责数据解析工作,将数据解析成我们想要的数据类型,这是一个接口对象,内部只有onParse(response: Response): T这一个方法,具体实现类有3个: SmartParserStreamParserOkResponseParser,第一个为万能的解析器,内部的toObservable/toAwait方法,就是通过它去实现的;第二是下载文件时用的的解析器;最后一个可同时返回请求头及对应的实体类。

工作流程图如下:

image.png

5、扩展

5.1、自定义Parser

前面第二部曲中,我们介绍了一系列toObserableXxx方法,通过该系列方法可以很方便的指定数据返回类型,特别是自定义的toObserableResponse(Class<T>)toObserableResponseList(Class<T>)toObserableResponsePageList(Class<T>)这3个方法,将Reponse<T>类型数据,处理的简直不要太完美,下面我们就来看看如何自定义Parser。

源码永远是最好的学习方式,在学习自定义Parser前,我们不妨先看看内置的Parser是如何实现的

SmartParser

open class SmartParser<T> : TypeParser<T> {
    protected constructor() : super()
    constructor(type: Type) : super(type)

    @Throws(IOException::class)
    override fun onParse(response: Response): T {
        return response.convert(types[0])
    }
}

其中,convert(Type)okhttp3.Response的扩展方法,定义如下:

@Throws(IOException::class)
fun <R> Response.convert(type: Type): R {
    val body = ExceptionHelper.throwIfFatal(this)
    val needDecodeResult = OkHttpCompat.needDecodeResult(this)
    LogUtil.log(this, null)
    val converter = OkHttpCompat.getConverter(this)
    return converter!!.convert(body, type, needDecodeResult)
}

该方法的目的就是把服务器返回的数据,转换为我们期望的实体类对象。

到这,我想大家应该就多少有点明白了,自定义Parser,无非就是继承TypeParser,然后实现onParser方法即可,现在,我们来自定义ResponseParser,用来处理Response<T>数据类型,先看看数据结构:

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

自定义ResponseParser代码如下:

@Parser(name = "Response", wrappers = [PageList::class])
open class ResponseParser<T> : TypeParser<T> {
    //注意,以下两个构造方法是必须的
    protected constructor() : super()
    constructor(type: Type) : super(type)

    @Throws(IOException::class)
    override fun onParse(response: okhttp3.Response): T {
        val data: Response<T> = response.convertTo(Response::class, *types)
        val t = data.data //获取data字段
        if (data.code != 200 || t == null) {
            throw ParseException(data.code.toString(), data.msg, response)
        }
        return t
    }
}

以上代码,需要注意两个地方

第一,我们在类开头使用了@Parser注解,该注解有两个参数,如下:

  • name 代表解析器的名称,这里取名为Response,于是在RxHttp类就会有toObservableResponse(Class<T>)方法(命名方式为:toObervable + name属性的值);

  • wrappers 可以把他理解为泛型T的包装类,需要传入Class[]数组对象,这里我们传入了{PageList.class}这两个类,于是就会生成 toObservableResponsePageList(Class<T>)方法。(命名方式为:toObserable +name属性的值+Class类名)

注:PageList类需要自己定义,用于加载分页数据,Demo里有这个类

第二,我们在if语句里,对code做了判断,非200或者data为空时,就抛出异常,并带上了code及msg字段,所以我们在异常回调的地方就能拿到这两个字段

此时,我们就可以通过这3个方法,直接拿到TList<T>PageList<T>类型数据,并且对code做了统一的判断,直接来看看如何使用这个解析器

//第一种方式,使用@parser注解生成的toObservableResponse方法
RxHttp.postForm("/service/...")   //发送post表单请求
    .add("key", "value")          //添加参数,可调用多次
    .toObservableResponse(Student.class)           //返回Student类型
    //.toObservableResponseList(Student.class)     //返回List<Student>类型
    //.toObservableResponsePageList(Student.class) //返回PageList<Student>类型
    .subscribe(student -> {   
        //请求成功,这里能拿到 Student对象               
    }, throwable -> {         
        //请求失败                
    });
    
//kotlin环境下,也可以直接使用toObservableResponse<T>方法,泛型可输入任意类型,如下:
RxHttp.postForm("/service/...")   //发送post表单请求
    .add("key", "value")          //添加参数,可调用多次
    .toObservableResponse<Student>()              //返回Student类型
    //.toObservableResponse<List<Student>>()      //返回List<Student>类型
    //.toObservableResponse<PageList<Student>>()  //返回PageList<Student>类型
    .subscribe({   
        //请求成功,这里能拿到 Student对象               
    }, {         
        //请求失败                
    }); 

//第二种方式,直接使用asParser(Parser<T>)方法
RxHttp.postForm("/service/...")   //发送post表单请求
    .add("key", "value")          //添加参数,可调用多次
    .toObserable(new ResponseParser<Student>(){})    //返回Student类型
    .subscribe(student -> {   
        //请求成功,这里能拿到 Student对象               
    }, throwable -> {         
        //请求失败                
    });  

以上两种方式,除了写法上的区别,其它都一样,相信大家都会选择第一种方式,不仅写法简单,还降低了耦合。

5.2、自定义Param

自定义Param,想较于自定义Parser,要更加的简单,我们只需根据自己的需求,继承NoBodyParam、FormParam、JsonParam等,增加或者重写方法即可,比如我们有以下3种情况,需要自定义Param,如下:

  • postForm请求,需要将所有添加的参数,拼接在一起,随后加密,最后将加密的字符串添加到请求头中
  • postJson请求,需要将所有的参数,也就是json字符串加密后再发送出去
  • FormParam里面的API不够用,我要自定义API
5.2.1、postForm请求加密

这种情况,我们需要继承FormParam,并重写getRequestBody()方法,如下:

@Param(methodName = "postEncryptForm")
public class PostEncryptFormParam extends FormParam {

    public PostEncryptFormParam(String url) {
        super(url, Method.POST);  //Method.POST代表post请求
    }

    @Override
    public RequestBody getRequestBody() {
        //所有body参数
        List<KeyValuePair> bodyParam = getBodyParam();
        //所有查询参数
        List<KeyValuePair> queryParam = getQueryParam();
        String encryptStr = "加密后的字符串";  //根据上面拿到的参数,自行实现加密逻辑
        addHeader("encryptStr", encryptStr);
        return super.getRequestBody();  //也可以自己构建RequestBody对象
    }
}
5.2.2、postJson请求加密

这种情况,我们需要继承JsonParam,也重写getRequestBody()方法,如下:

@Param(methodName = "postEncryptJson")
public class PostEncryptJsonParam extends JsonParam {

    public PostEncryptJsonParam(String url) {
        super(url, Method.POST);
    }

    @Override
    public RequestBody getRequestBody() {
        //这里拿到你添加的所有参数
        Map<String, Object> params = getBodyParam();
        String encryptStr = "加密后的字符串";  //根据上面拿到的参数,自行实现解密逻辑
        return RequestBody.create(MEDIA_TYPE_JSON, encryptStr);  //发送加密后的字符串
    }
}
5.2.3、自定义API

我们继承FormParam,并新增两个test方法`,如下:

@Param(methodName = "postTestForm")
public class PostTestFormParam extends FormParam {

    public PostTestFormParam(String url) {
        super(url, Method.POST);
    }
    
    public PostTestFormParam test(long a, float b) {
        //这里的业务逻辑自行实现
        return this;
    }
    
    public PostTestFormParam test1(String s, double b) {
        //这里的业务逻辑自行实现
        return this;
    }
}
5.2.4、使用自定义的Param

同样的问题,我们怎么用这3个自定义的Param呢?我想大多数人在类名前发现类@Param注解,并为Param取了别名。那这个又有什么作用呢? 答案揭晓,只要在自定的Param上使用了@Param注解,并取了别名,就会在RxHttp类自动生成一个跟别名一样的方法,在上面我们自定义了3个Param,并分别取别名为postEncryptForm、postEncryptJson、postTestForm,此时就会在RxHttp类中生成postEncryptForm(String)postEncryptJson(String)postTestForm(String)这3个方法,我们在RxHttp这个类中来看下:

  public static RxHttp$PostEncryptFormParam postEncryptForm(String url) {
    return new RxHttp$PostEncryptFormParam(new PostEncryptFormParam(url));
  }
  
  public static RxHttp$PostEncryptJsonParam postEncryptJson(String url) {
    return new RxHttp$PostEncryptJsonParam(new PostEncryptJsonParam(url));
  }

  public static RxHttp$PostTestFormParam postTestForm(String url) {
    return new RxHttp$PostTestFormParam(new PostTestFormParam(url));
  }

发请求时,只需要调用对应的方法就好,如:

//发送加密的postForm请求
RxHttp.postEncryptForm("/service/...")   
    .add("key", "value")          //添加参数,可调用多次
    .toObservableString()                  //返回String类型
    .subscribe(s-> {   
        //请求成功    
    }, throwable -> {         
        //请求失败                
    });  
    
//发送加密的postJson请求
RxHttp.postEncryptJson("/service/...")   
    .add("key", "value")          //添加参数,可调用多次
    .toObservableString()                  //返回String类型
    .subscribe(s-> {   
        //请求成功    
    }, throwable -> {         
        //请求失败                
    });  

那我自定义的API如何调用呢,so easy!!!!,选择对应的请求方法后,就可以直接调用,如下:

//发送加密的postJson请求
RxHttp.postTestJson("/service/...")   
    .test(100L, 99.99F)          //调用自定义的API
    .test1("testKey", 88.88D)    //调用自定义的API
    .add("key", "value")         //添加参数,可调用多次
    .toObservableString()                  //返回String类型
    .subscribe(s-> {   
        //请求成功    
    }, throwable -> {         
        //请求失败                
    });  

5.3、自定义Converter

RxHttp内部默认使用来GsonConverter,并且额外提供了6个Converter,如下:

//非必须 根据自己需求选择Converter  RxHttp默认内置了GsonConverter
implementation "com.github.liujingxing.rxhttp:converter-serialization:$rxhttp_version"
implementation "com.github.liujingxing.rxhttp:converter-fastjson:$rxhttp_version"
implementation "com.github.liujingxing.rxhttp:converter-jackson:$rxhttp_version"
implementation "com.github.liujingxing.rxhttp:converter-moshi:$rxhttp_version"
implementation "com.github.liujingxing.rxhttp:converter-protobuf:$rxhttp_version"
implementation "com.github.liujingxing.rxhttp:converter-simplexml:$rxhttp_version"
5.3.1、自定义TestConverter

即使这样,RxHttp也无法保证满足所有的业务需求,为此,我们可以选择自定义Converter,自定义Converter需要继承IConverter接口,如下:

public class TestConverter implements IConverter {

    /**
     * 请求成功后会被回调
     * @param body             ResponseBody
     * @param type             泛型类型
     * @param onResultDecoder  是否需要对结果进行解码/解密
     */
    @Override
    public <T> T convert(ResponseBody body, Type type, boolean onResultDecoder) throws IOException {
        //自行实现相关逻辑
        return null;
    }

    /**
     * json请求前会被回调,需要自行根据泛型T创建RequestBody对象,并返回
     */
    @Override
    public <T> RequestBody convert(T value) throws IOException {
        //自行实现相关逻辑
        return null;
    }
}

以上两个convert方法根据自身业务需求自行实现,可以参考RxHttp提供FastJsonConverter、SimpleXmlConverter等Converter

5.3.2、怎么用Converter

请查看本文3.10章节----设置Converter

6、小技巧

在这教大家一个小技巧,由于使用RxHttp发送请求都遵循请求三部曲,故我们可以在android studio 设置代码模版,如下在这里插入图片描述 如图设置好后,写代码时,输入rp,就会自动生成模版,如下: 在这里插入图片描述

7、小结

到这,RxHttp常用功能介绍完毕,你会发现,一切都是那么的美好,无论你是get、post、加密请求、自定义解析器,还是文件上传/下载/进度监听等等,皆遵循请求三部曲。特别是对Response<T>类型数据处理,可以说是天衣无缝,我们无需每次都判断code,直接就可以拿到T,简直了。。。

最后,喜欢的,请给本文点个赞,如果可以,还请给个star,创作不易,感激不尽。🙏🙏🙏