先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新HarmonyOS鸿蒙全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img

img
img
htt

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上鸿蒙开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip204888 (备注鸿蒙)
img

正文

.asClass(Student.class) //返回Student类型
.subscribe(student -> {
//请求成功,这里就能拿到 Student对象
}, throwable -> {
//请求失败
});

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

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

解析Response<T>类型数据

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

public class Response {
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类型
.subscribe(students -> {
//请求成功,这里能拿到List列表对象
}, throwable -> {
//请求失败
});

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

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

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

RxHttp.postForm(“/service/…”) //发送post表单请求
.add(“key”, “value”) //添加参数,可调用多次
.asResponsePageList(Student.class) //返回PageList类型
.subscribe(pageList -> {
//请求成功,这里能拿到PageList列表对象
int totalPage = pageList.getTotalPage(); //总页数
List 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方法,如,asInteger、asBoolean、asLong、asBitmap、asList、asMap等等,它们最终都是通过asParser(Parser<T>)方法实现的,具体实现过程,这里先跳过,后续会详细讲解。

3.3.3、第三部曲:订阅回调

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

//不处理任何回调
RxHttp.postForm(“/service/…”) //发送post表单请求
.add(“key”, “value”) //添加参数,可调用多次
.asResponseList(Student.class) //返回List类型
.subscribe(); //不订阅任何回调

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

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

//等等,省略

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

RxHttp.get(“/service/…”)
.asString()
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe(disposable -> {
//请求开始,当前在主线程回调
})
.doFinally(() -> {
//请求结束,当前在主线程回调
})
.as(RxLife.as(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() {
SSLParams sslParams = HttpsUtils.getSslSocketFactory();
return new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.sslSocketFactory(sslParams.sSLSocketFactory, sslParams.trustManager)
.hostnameVerifier((hostname, session) -> true)
.build();
}

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

3.5、公共参数/请求头

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

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

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

当然,如果希望某个请求不回调该接口,即不添加公共参数/请求头,则可以调用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”) //非默认域名,并取别名为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/…”)
.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,先来看看如何用:

//以下代码均在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();

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

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 -> {
//上传失败
});

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

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

可以看到,跟上传的代码相比,我们仅仅增加了upload方法,第一个参数是指定回调的线程,这里我们指定了在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 -> {
//下载失败
});

如果你想监听下载进度,调用asDownload(String, Scheduler, Consumer<Progress>)方法,传入回调线程及回调接口即可,如下:

//文件存储路径
String destPath = getExternalCacheDir() + “/” + System.currentTimeMillis() + “.apk”;
RxHttp.get(“http://update.9158.com/miaolive/Miaolive.apk”)
.asDownload(destPath, AndroidSchedulers.mainThread(), 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、断点下载

断点下载相较于下载,仅需要调用asAppendDownload方法即可,其它没有任何差别

String destPath = getExternalCacheDir() + “/” + “test.apk”;
RxHttp.get(“http://update.9158.com/miaolive/Miaolive.apk”)
.asAppendDownload(destPath)
.subscribe(s -> { //s为String类型
//下载成功,处理相关逻辑
}, throwable -> {
//下载失败,处理相关逻辑
});

当然,此时你想监听下载进度,也是可以的,调用asAppendDownload(String, Scheduler, Consumer<Progress>)方法,传入回调线程及回调接口即可,如下:

String destPath = getExternalCacheDir() + “/” + “test.apk”;
RxHttp.get(“http://update.9158.com/miaolive/Miaolive.apk”)
.asAppendDownload(destPath, AndroidSchedulers.mainThread(), progress -> {
//下载进度回调,0-100,仅在进度有更新时才会回调
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/…”)
.asString()
.timeout(5, TimeUnit.SECONDS)//设置总超时时间为5s
.as(RxLife.asOnMain(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
.asClass(NewsDataXml.class)
.as(RxLife.asOnMain(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
.asString()
.subscribe(s -> {
//成功回调
}, (OnError) error -> {
//失败回调
});

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

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

3.12.1、同步请求

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

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

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

3.12.2、指定回调所在线程

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

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

3.13、 Retrofit用户

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

public class HttpWrapper {

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

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

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

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

public class HttpWrapper {

//单个占位符
public static Observable getStudent(int page) {
return RxHttp.get(“/service/%d/…”, page) //使用标准的占位符协议
.asClass(Student.class);
}

//多个占位符
public static Observable getStudent(int page, int count) {
return RxHttp.get(“/service/%1 d / d/%2 d/d/…”, page, count) //使用标准的占位符协议
.asClass(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等新版本,一样可以兼容,而且非常简单。

RxHttp v2.4.2以下版本中,对OkHttp的兼容,也使用了该方式去适配okhttp 各个版本,为此,RxHttp适配了OkHttp v3.12.0v4.9.0(截止2020/12/27最新版本)中的任一版本(v4.3.0除外,该版本有一个bug,导致无法适配),因此,使用RxHttp,完全不用担心okhttp版本冲突问题。

同时兼容RxJavaOkHttp不同版本,这就是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”)
.toClass()
.await()

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

RxHttp工作流程

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

  • RxHttp:这是最重要的一个角色,所以请求的唯一入口,内部持有一个Param对象,它的职责是,请求参数/请求头/BaseUrl的处理,请求线程的调度,提供注解生成的方法等等,最终的使命就是通过Param构建一个okhttp3.Request对象,随后在构建一个okhttp3.Call对象,并把Call对象丢给ObservableIAwait,然后由ObservableIAwait真正的去执行请求
  • Param:它的职责是处理请求参数/请求头/url等一切用来构建okhttp3.Request需要的东西,最终使命就是构建okhttp3.Request对象,它被RxHttp类所持有,RxHttp把构建okhttp3.Request对象所需要的东西,都交给Param去实现的
  • IAwiat:结合协程发请求时,真正执行网络请求的对象,具体实现类为AwaitImpl,它内部持有Parser对象,请求返回后,将okhttp3.Response丢给Parser去解析,并返回解析后的对象
  • Observable:结合RxJava发送请求时,真正执行网络请求的对象,具体实现类有ObservableCallExecuteObservableCallEnqueueObservableParser,分别用于同步请求、异步请求及请求返回后对okhttp3.Response对象的数据解析,ObservableParser内部持有Parser对象,具体的解析工作都交给Parser
  • Parser:负责数据解析工作,将数据解析成我们想要的数据类型,这是一个接口对象,内部只有onParse(response: Response): T这一个方法,具体实现类有4个: SimpleParserStreamParserSuspendStreamParserBitmapParser,第一个为万能的解析器,内部的asClass/toClss方法,就是通过它去实现的;第二第三是下载文件时用的的解析器,区别前者是结合RxJava下载的,后者是结合协程下载的;最后一个是解析Bitmap对象用的,asBitmap/toBitmap就是通过它去实现的

工作流程图如下:

5、扩展

5.1、自定义Parser

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

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

SimPleParser

open class SimpleParser : TypeParser {
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 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 {
private int code;

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注鸿蒙)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

e) : super(type)

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

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

@Throws(IOException::class)
fun 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 {
private int code;

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注鸿蒙)
[外链图片转存中…(img-IgySa3B5-1713152599711)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐