RxJava练武场是一个rxjava在项目中应用的小系列,包括:java
两个问题:json
Retrofit已经对网络请求作了封装,为何还要封装?api
答:网络请求中对于请求流程、配置、入参封装、加解密、异常处理每一个app都是固定不变的,若是业务每次请求都本身处理,会存在冗余代码,且质量不易保证。因此咱们须要基于Retrofit对这些流程和操做作二次封装,并对调用方式进行统一,咱们称之为网络框架。bash
框架封装Observable方式是什么?服务器
答:传统网络框架封装方式采用callback/listener异步回调网络请求结果。可是这种callback的方式,没有利用到Retrofit的一大优点--rxjava调用,因此咱们要基于rxjava调用方式,封装一个基于返回Observable的网络请求框架。 如下所说网络框架,均指基于返回Observable的网络请求二次封装框架。网络
设计原则:架构
网络框架Api返回Observable对象,并做为网络请求事件的生产者:app
生产者负责请求的发起接收,和返回数据的预处理。框架
业务注册Observer类,做为消费者。异步
只负责对返回数据的响应
这是为了T3的设计目标,生产者和消费者界限明确,作到彻底解耦,为网络请求与其余rxjava异步调用的整合打基础。
这篇文章详细讲述框架中生产者的实现,消费者的实如今RxJava练武场之——Observable网络框架的解耦和复用中介绍
首先咱们须要一个Manager或Helper全局句柄,经过他能够发起网络请求,通常设计为单例全局持有,有利于网络请求一些资源的共用。 咱们暂定为NetHelper,其网络请求Api定义为:
private static <R extends HttpResponse<?>> Observable sendRequest(final HttpRequest<R> orgRequest)
复制代码
sendRequest方法中,返回Observable对象,经过泛型规范Response的类型(T2目标的基础),HttpRequest(T1目标的基础)
定义Request接口,这个接口是retrofit指定的方式。Retrofit原本但愿你这么作的:
public interface LoginRequest {
@POST("{/get/login}")
Observable<LoginInfo> login(@FieldMap Map<String, String> params);
}
复制代码
在LoginRequest中你定义了url,Response JavaBean, 入参params的方式(map) 经过 LoginRequest loginRequest = retrofit.create(LoginRequest.class);
动态代理的方式帮你把request的配置都生成好了。
可是map的传入这部分,须要业务自行处理入参的组合和加密,固然业务能够经过工具类的方式来规范这一步骤(网上可能是此种方案),但这样会让业务参与到请求流程来了,不符合T4的目标。
咱们但愿业务定义LoginRequest以下:
public class LoginRequest extends HttpRequest<R extends HttpResponse> {
private String userAccount;
private String userPwd;
private int accountType;
@Override
protected String getURL() {
return "/get/login";
}
}
复制代码
业务只关心入参和url的定义,不须要关心入参的组合和加密方式(T1目标),经过NetHelper.send(loginRequest)一个调用就能拿到结果。这种定义方式不只和Retrofit原生方式同样将url和Response类型都定义了,还一块儿将params也定义了进来。
因此咱们不直接使用Retrofit的接口定义方式,封装了一个专为业务定义的HttpRequest类,最终也由这个类向Retrofit接口传递数据。
要知足上述调用方式,知足两个前提条件
Request接口定义以下:
public interface Request {
@POST("{url}")
Observable<JSONObject> postJSONResult(@Path(value="url",encoded =true) String url, @FieldMap Map<String, String> params);
}
复制代码
咱们定义的Observable泛型是通用的JSONObject,url是经过入参方式传入。
NetHelper.java中
// 初始化okhttp
OkHttpClient client = new OkHttpClient.Builder()
.build();
/**
*OkHttpClient每次请求的时候都要建立,注意:OkHttpClient.Builder()中有ConnectionPool做为OkHttp的 *链接池要复用,不然请求过多时容易致使内存溢出
**/
// 初始化Retrofit
retrofit = new Retrofit.Builder()
.client(client)
.baseUrl(Request.HOST)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(MyConverterFactory.create())
.build();
复制代码
通常addConverterFactory配置是GsonConverterFactory,用于把Response经过GSon转为javaBean(Request接口中定义的类型),因为咱们定义为了JsonObject,因此咱们使用MyConverterFactory自定义实现
Observable的真正建立,咱们放到Request基类中
public abstract class HttpRequest<R extends HttpResponse>{
protected abstract String getURLAction();
//Observable的建立
public Observable getObservable(Retrofit retrofit) {
Request request = retrofit.create(Request.class);
return request.postJSONResult(getURLAction(),getURLParam());
}
//子类中复写该接口的url
public String getBaseURL(){
//return base url;
}
public SortedMap<String, String> getURLParam() {
//return 对HttpRequest子类(业务类),定义的params,进行组合和加密
//通用的组合和加密逻辑就在此处。
}
//response类型解析
protected Type type;
public ObservableMapiRequest(BaseContext context) {
super(context);
initType();
}
//泛型R类型的获取,拿到HttpResponse的类型
private void initType() {
Type superClass = getClass().getGenericSuperclass();
this.type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
}
public Type getType() {
return type;
}
}
复制代码
这一步中,咱们将HttpRequest子类(例如LoginRequest)中定义的url params HttpResponse都获取到了。
以上三步,已经初步将Observable返回。下面的处理NetHelper中Api的调用,也是框架的重点和核心:
private static <R extends HttpResponse<?>> Observable sendRequest(final ObservableRequest<R> orgRequest){
return NetHelper.getObservable(orgRequest)
//对Response的JsonObject进行类型转化
.map(new JavaBeanFunc(orgRequest.getType()))
//添加拦截器、Log等
.filter(new LogInterceptor(orgRequest))
//对Response预处理,code分类等
.compose(ResponseTransformer.handleResult())
//配置线程
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
复制代码
NetHelper.getApiObservable方法后,再加上网络请求的线程配置,这时候业务subscribe消费者,就能够直接获得解密后的JsonObject了。注意此时只是JSONObject,咱们须要转换成咱们须要的Response。
咱们在HttpRequest类中看到了,定义HttpRequest子类时,是须要传入Response类型的(就是R),在HttpRequest类中已经将Response的类型解析出来,并保存了。在JavaBeanFunc中进行了转化
public class JavaBeanFunc<J extends JSONObject,T extends HttpResponse> implements Function<J,T> {
Type mCon;
public JavaBeanFunc(Type con){
mCon = con;
}
@Override
public T apply(J jsonObject) throws Exception {
if (jsonObject != null){
T response = jsonObject.toJavaObject(mCon);
return response;
} else {
return null;
}
}
}
复制代码
同时配置了拦截器,用于log输出,和异常的拦截,也能够作网络性能指标的记录等
public class LogInterceptor<R extends HttpResponse<?>> implements Predicate<R> {
private HttpRequest<R> orgRequest;
public LogInterceptor(HttpRequest<R> request){
orgRequest = request;
}
@Override
public boolean test(R response) throws Exception {
boolean isPredicate = false;
if (orgRequest != null && response != null) {
HttpHelper.printHttpLog(orgRequest, JSONObject.toJSONString(response));
isPredicate = true;
}
return isPredicate;
}
}
复制代码
response解析完毕以后,尚未完事;必须对response的正常和异常两种状况作一个判断,哪些状况做为onNext传递,哪些状况做为onError传递,也要定义清楚。此处咱们采用ObservableTransformer来对数据流处理。
有些文章对于progressbar的控制也放到这里,我认为并不符合框架的设计原则,也就没法实现目标T3,不属于生产者该作的事情。
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));
}
}
}
复制代码
至此,你可能有两个疑问,一个是response中code的断定能够在observer中处理吗,另外一个是服务器错误和业务错误为什么都做为error抛出。
第一个问题: code值的断定不能够在observer中处理,而必须在Observable一端处理。由于Observable形式的网络请求是做为数据流中的一环出现的,可能当前网络请求只是一连串异步调用(rxjava调用)的一环。这是实现目标T3的关键点。 第二个问题: response中code!=SUCCESS是业务错误的状况,必须向数据流中发出,让业务处理此异常。(那同时对于Response的定义也是,code!=SUCCESS必须是不须要业务处理的状况才行) 两种错误都抛出error(内部code不一样),方便架构使用者在事件响应时,既能捕捉全部错误,又能区分错误的类型。
看下最终使用的状况
LoginRequest request = new LoginRequest();
request.setUserAccount("888");
request.setUserPwd("888");
ApiHelper.send(request)
.subscribe(new Consumer<LoginResponse>() {
@Override
public void accept(LoginResponse s) throws Exception {
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
}
});
复制代码
业务在使用上很简洁,LoginRequest和LoginResponse的定义也是业务关注的内容。基本达到T4的目标
Request的cancle机制,通常原理是这样:在Activity/Fragment关闭时,须要cancle调正在请求中的网络,因此须要BaseActivity/BaseFragment的支持。 方案有二,这篇文章就不赘述了,由于都是比较成熟的方案。
咱们经常遇到这样一种场景,一个网络请求每每基于上一个网络请求的结果,或者一个页面的数据来自两个网络请求的组合,咱们的网络框架是如何知足这种需求的呢?这也是咱们这套框架设计的出发点和落脚点:
RegisterRequest registerRequest = new RegisterRequest();
registerRequest.setUserAccount(account.getText());
registerRequest.setUserPwd(pwd.getText());
registerRequest.setUserSex(sex.getType());
ApiHelper
.send(registerRequest)
.flatMap(new Function<RegisterResponse, ObservableSource<HttpResponse<LoginResponse>>>() {
@Override
public ObservableSource<HttpResponse<LoginResponse>> apply(RegisterResponse response) throws Exception {
LoginRequest loginRequest = new LoginRequest();
loginRequest.setUserAccount(account.getText());
loginRequest.setUserPwd(pwd.getText();
return ApiHelper.send(loginRequest);
}
})
.subscribe(new Consumer<LoginResponse>() {
@Override
public void accept(LoginResponse s) throws Exception {
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
}
});
复制代码
这样咱们就完成了一个先注册后登录的网络请求流程,将网络框架融入了rxjava的调用链之中。
这篇文章着重对基于Observable的网络框架中,Observable生产者部分进行了阐述,在‘框架的封装性’一节所举的例子中,咱们只采用了最普通的Consumer来进行事件的消费,咱们在下一篇文章中RxJava练武场之——Observable网络框架的解耦和复用会专门对消费者进行封装,使该框架的规范性和扩展性更强。