基于RxJava Retrofit网络框架的搭建
RxJava、Retrofit两个第三方库的优势
RxJava的使用场景
本文讨论的如无特殊说明,均指代rxjava2 和 retrofit2。
我们先讨论一下rxJava引入的背景。有一些应用场景,特别是一些复杂的异步回调场景,如果使用传统的开发模式,会如何实现。
列举一些场景:
- EditText: 根据输入内容自动搜索时,只有当输入间隔大于某个duration时,才触发搜索。
同时要根据输入内容,实时判断其有效性。
对输入的内容,实时做出改变(比如不能输入数字,特殊字符等)
(https://www.jianshu.com/p/9aaccd7bb600) - 联合判断
https://www.jianshu.com/p/88ce90240396 - 注册--登录--获取用户信息(注册的一般流程) 获取权限--获取经纬度--鉴权--生成订单(生成订单一般流程) 这样一连串的网络请求。
- 结合多个接口的数据,再更新UI;或者历史记录、购物车记录等,需要合并本地缓存和网络请求返回的数据;
- 网络请求前置条件token的获取,token可以本地缓存,本地缓存拿不到要去网络获取。同时对于token拿不到时的网络请求先等待,token获取后将等待的请求一一发出。甚至token请求需要设置重试次数,超过次数才停止请求
- 防重复操作,倒计时
- 多重缓存处理:先读取缓存更新UI,在网络请求更新UI(https://www.jianshu.com/p/7474950af2df)
- 多处场景需要同一个监听事件,但是每个场景注册时间点不同,如何做到事件产生之后注册的监听者也能收到回调。
这些应用场景几乎是每个app都会遇到的,传统的处理方式:层层递进(回调),各种标志位控制,嵌套的if else,各种异步调用分散在代码各处,要将各个回调结果汇总,并且综合处理回调的不同状态。而且由于异步回调的特点,代码处理方式不会像同步调用那样直接可控,异步回调导致的时序问题又将问题复杂度提高了几个level。我经常会想,这种callback的异步调用方式,为何会将代码变得丑陋不堪和难以维护?设计模式中只提供了观察者模式,但是对于观察者回调复杂后,没有提出更好的解决方案。RxJava(或者说响应式编程)主要解决了这类的问题。
关于RxJava是如何解决以上场景中遇到的问题,以及如何和retrofit一起联合使用,短短几行无法说不清,在基于RxJava Retrofit的网络框架(二)中会详解。
Retrofit的使用
Retrofit并不实现网络请求本身(网络请求由okhttp负责),他是一个框架,为网络请求本身提供可扩展,可配置,易使用的外部封装。okhttp负责提高网络请求的性能和兼容性,Retrofit负责更好的使用他。框架的重要性不言而喻,好的框架让使用者关注更少细节,轻易的扩展。okhttp好像汽车的发动机,对于汽车性能和稳定性起到决定性作用,框架就是除了发动机以外其他部分,除了对发送机功能的整合外,漂亮的外观,舒适易用的体验才是人们愿意开这台车的原因。
以下列举Retrofit相对于volley Android-Async-Http等框架的优势
- 简洁易用:通过注解配置网络参数,大量设计模式(建造者模式,工厂)简化配置和使用。
- 功能强大:支持RxJava方式(也支持callback方式),支持同步&异步,
- 耦合度低,扩展性好:模块高度封装,彻底解耦。
由于Android-Async-Http的停更,Google对volley基本放弃的态度,这两套框架使用者和价值日渐减少。反观okhttp,Google官方应用则广泛使用。作为okhttp的黄金搭档(同为square公司出品),Retrofit可以说是目前Android app网络请求框架的不二选择,与okhttp搭配,是性能、稳定性、兼容性、易用性都达到很高水平的框架组合。
Observable网络框架的抽象
Observable网络框架建立的原因
- Retrofit已经对网络请求做了封装,为什么还要封装?
网络请求中对于请求流程、配置、加解密、异常处理对于每个app都是固定不变的,如果业务每次请求都自己处理逻辑,会存在冗余代码,且质量不易保证。所以我们需要基于Retrofit对请求流程、配置、加解密、异常处理等操作做二次封装,并对调用方式进行统一。 - 框架封装方式Observable是什么?
对网络请求二次封装(一般为异步请求),传统使用callback方式异步回调网络请求结果。但是这种callback的方式,没有利用到Retrofit的一大优势--rxjava调用,所以我们要基于rxjava调用方式,封装一个基于Observable的网络请求框架。
以下所说网络框架,均指基于Observable的网络请求二次封装框架。
Observable网络框架要解决的问题
网络框架要帮助业务处理以下几个问题:
- 支持Get Post请求,对Request的参数业务可轻松配置
- 对Request 参数做发送前处理:组合和加密处理
- 返回Response 解密处理,Java实体化
- 返回Response code码判断及处理
- 网络请求cancle机制 progressBar配置等通用处理
达到的目标:业务使用框架时,只需要关注业务相关(Request参数,Response返回值的配置和处理),其他都交给框架处理。同时对于网络请求的属性可配置(error是否提示,progressBar是否显示等)
Observable网络框架如何实现
设计原则:
- 网络请求Api返回Observable对象,作为网络请求事件的生产者:
生产者负责请求的发起,和返回的所有预处理。 - 为业务提供BaseObserver类,使用者实现其子类作为消费者
消费者基类提供对response的一般处理,消费者业务也可以使用自己Observer处理
在Observable的创建过程中,框架如何封装?
首先我们需要一个Manager或Helper全局实例,通过他可以发起网络请求,一般设计为单例全局持有,有利于网络请求一些资源的共用。
我们暂定为NetHelper,其网络请求接口定义为:
private static Observable<R> sendRequest(final HttpRequest request, final TypeReference<R> t)
sendRequest方法中,我们来看下Observable对象的生成过程:此处我们基于Retrofit本身Observable生成方式,我们先看下Retrofit最基础是如何创建Observable的。
第一步,定义Request,Request类的定义在Retrofit里通过注解的方式完成的
public interface Request {
@POST("{url}")
Observable<JSONObject> postJSONResult(@Path(value="url",encoded =true) String url, @FieldMap Map<String, String> params);
}
可以看到我们的定义方式是通用的(每个request都可以复用),每个request都是通过postJSONResult方法获取observable,传入自己的url和params即可完成不同的网络请求。
第二步,创建retrofit
NetHelper.java中
// 初始化okhttp
OkHttpClient client = new OkHttpClient.Builder()
.build();
// 初始化Retrofit
retrofit = new Retrofit.Builder()
.client(client)
.baseUrl(Request.HOST)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(MyConverterFactory.create())
.build();
OkHttpClient每次请求的时候都要创建,注意:OkHttpClient.Builder()中有ConnectionPool作为OkHttp的连接池要复用,否则请求过多时容易导致内存溢出。
创建Retrofit实例过程中,设置了okHttpClient,baseUrl,调用方式rxJava(通过addCallAdapterFactory)
GsonConverterFactory这些都是一般的写法,GsonConverterFactory作用是把Response通过GSon转为javaBean。App业务中一般是先解密后Gson转,所以此处使用MyConverterFactory实现解密功能。
第三步,生成Observable
这一步是生成observable的过程,与httpRequest本身有关(我们前面提到了Request类是一个支持Retrofit通用类,业务自定义的请求类HttpRequest实现了 getURLParam() getURLAction()等方法),所以这个获取Observable的方法可以放到HttpRequest中进行(NetHelper.sendRequest方法是传入了HttpRequest对象的)
Request request = retrofit.create(Request.class);
return request.postJSONResult(getURLAction(),getURLParam());
对于httpRequest中入参的组合和加密,实现在getURLParam()方法里。
备注:我们的网络post请求params是query形式的,如果是body表单,还需要另外的处理方式。
以上三步,已经初步将Observable返回。通过以上几步只是基于Retrofit自身的Observable创建方法做了一些封装。下面的处理是框架的重点和核心:
private static Observable<R> sendRequest(final HttpRequest request,final TypeReference<R> t){
return NetHelper.getApiObservable(request)
.compose(ResponseTransformer.handleResult())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
注意:sendRequest方法一定要Observable所有的链式操作执行完后在返回。
NetHelper.getApiObservable方法后,再加上网络请求的线程配置,这时候业务subscribe消费者,就可以直接得到解密后的JsonObject了。注意此时是string而不是Retrofit通常的JavaBean,这是因为我们要定义一个通用的Request类,将其接口返回值定义为了Observable<JSONObject>,所以我们还需要一步转换。
.map(new Function<JSONObject,R>() {
@Override
public R apply(JSONObject jsonObject) throws Exception {
if (jsonObject != null){
R response = jsonObject.toJavaObject(con);
if (orgRequest != null) {
HttpHelper.printHttpLog(orgRequest, jsonObject.toString());
}
return response;
} else {
return null;
}
}
})
response的异常处理,progressbar的显示等,也需要架构统一处理。我们引入了ResponseTransformer,你可以把他理解为map操作符,在交给消费者前对response结果做了处理。
public static <T extends Serializable> ObservableTransformer<T, T> handleResult() {
return upstream -> upstream
.onErrorResumeNext(new ErrorResumeFunction<T>())
.flatMap(new ResponseFunction<T>());
}
private static class ErrorResumeFunction<T extends Serializable> implements Function<Throwable, ObservableSource<? extends T>> {
@Override
public ObservableSource<? extends T> apply(Throwable throwable) throws Exception {
return Observable.error(CustomException.handleException(throwable));
}
}
private static class ResponseFunction<T extends Serializable> implements Function<T, ObservableSource<T>> {
@Override
public ObservableSource<T> apply(T tResponse) throws Exception {
int code = tResponse.getCode();
String message = tResponse.getMsg();
if (code == SUCCESS.value()) {
return Observable.just(tResponse);
} else {
return Observable.error(new ApiException(code, message));
}
}
}
可以看出对于事件流upstream,做了正常和异常的再分流,对于服务器错误(超时,404等)通过onErrorResumeFunction继续抛出Observable.error,
对于正常返回,根据response中code的定义,只有SUCCESS时才返回数据Observable.just(),其他情况(业务错误等)作为错误情况继续抛出。
你可能有两个疑问,一个是response中code的判定可以在observer中处理吗,另一个是服务器错误和业务错误为何都作为error抛出。
第一个问题:
code值的判定不可以在observer中处理,而必须在Observable一端处理。因为Observable形式的网络请求是作为数据流中的一环出现的,可能当前网络请求只是一连串异步调用(rxjava调用)的一环。
第二个问题:
response中code!=SUCCESS是业务错误的情况,必须向数据流中发出,让业务处理此异常。(那同时对于Response的定义也是,code!=SUCCESS必须是不需要业务处理的情况才行)
两种错误都抛出error(内部code不同),方便架构使用者在事件响应时,既能捕捉所有错误,又能区分错误的类型。
哪些处理放到了BaseObserver中?
BaseObserver顾名思义,是架构使用者在rxjava流式调用最后一步所使用的观察者基类,他适合将网络请求的UI响应放入其中。
public abstract class BaseObserver<T > implements Observer<T>{
/**
* 请求成功
* @param t
*/
public abstract void onSuccess(T t);
/**
* 请求失败
* @param
* @param object
*/
public abstract void onFail(ApiException);
@Override
public void onSubscribe(Disposable d) {
if (isShowProgress()) {
showProgress(true);
}
}
@Override
public void onNext(T t) {
if (isShowProgress()) {
showProgress(false);
}
onSuccess(t);
}
@Override
public void onError(Throwable e) {
if (isShowProgress()) {
showProgress(false);
}
if (e instanceof ApiException){
ApiException apiException = (ApiException)e;
switch(apiException.getCode()){
case:NOT_LOGIN
break;
case:TOKEN_ERROR
break;
}
}
}
/**
* 网络请求是否loading显示
* @return
*/
protected boolean isShowProgress(){
return true;
}
}
BaseObserver中,我们可以看到处理了showProgress,ApiException做了处理。同时可以对progressBar是否显示做配置。可以满足一般的网络请求架构使用,当然也可以自行subscribe自己的Observer。
网友评论