RxJava練武場之——基於Observable網路框架的搭建

challengerwang發表於2019-01-02

RxJava練武場之——基於Observable網路框架的搭建

RxJava練武場是一個rxjava在專案中應用的小系列,包括:

Observable網路框架設計的背景和原則

Observable網路框架建立的原因

兩個問題:

  • Retrofit已經對網路請求做了封裝,為什麼還要封裝?

    答:網路請求中對於請求流程、配置、入參封裝、加解密、異常處理每個app都是固定不變的,如果業務每次請求都自己處理,會存在冗餘程式碼,且質量不易保證。所以我們需要基於Retrofit對這些流程和操作做二次封裝,並對呼叫方式進行統一,我們稱之為網路框架。

  • 框架封裝Observable方式是什麼?

    答:傳統網路框架封裝方式採用callback/listener非同步回撥網路請求結果。但是這種callback的方式,沒有利用到Retrofit的一大優勢–rxjava呼叫,所以我們要基於rxjava呼叫方式,封裝一個基於返回Observable的網路請求框架。以下所說網路框架,均指基於返回Observable的網路請求二次封裝框架。

Observable網路框架設計目標

  • T1:業務對Request的業務params、url可輕鬆配置,框架對params組合和加密統一處理
  • T2:框架對返回Response解密、code判斷等統一處理,並直接返回業務JavaBean結果
  • T3:返回值Observable,使網路請求可作為rxjava呼叫鏈中的一環
  • T4:呼叫入口統一、封裝流程,對業務透明
  • T5:請求支援cancle機制、progressBar等通用處理

Observable網路框架設計原則

設計原則:

  1. 網路框架Api返回Observable物件,並作為網路請求事件的生產者:

    生產者負責請求的發起接收,和返回資料的預處理。

  2. 業務註冊Observer類,作為消費者。

    只負責對返回資料的響應

這是為了T3的設計目標,生產者和消費者界限明確,做到完全解耦,為網路請求與其他rxjava非同步呼叫的整合打基礎。

這篇文章詳細講述框架中生產者的實現,消費者的實現在RxJava練武場之——Observable網路框架的解耦和複用中介紹


Observable網路框架如何實現

在Observable的建立過程中,框架如何封裝?

首先我們需要一個Manager或Helper全域性控制程式碼,通過他可以發起網路請求,一般設計為單例全域性持有,有利於網路請求一些資源的共用。我們暫定為NetHelper,其網路請求Api定義為:

private static <
R extends HttpResponse<
?>
>
Observable sendRequest(final HttpRequest<
R>
orgRequest) 複製程式碼

sendRequest方法中,返回Observable物件,通過泛型規範Response的型別(T2目標的基礎),HttpRequest(T1目標的基礎)

第一步 HttpRequest定義

定義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介面傳遞資料。

要滿足上述呼叫方式,滿足兩個前提條件

  1. HttRequest基類,要處理入參的組合和加密處理
  2. Retrofit對應的Request定義介面,要改為通用的方式,以適應不同的HttpRequest子類的定義

Request介面定義如下:

public interface Request { 
@POST("{url
}"
) Observable<
JSONObject>
postJSONResult(@Path(value="url",encoded =true) String url, @FieldMap Map<
String, String>
params);

}複製程式碼

我們定義的Observable泛型是通用的JSONObject,url是通過入參方式傳入。

第二步 建立retrofit;

NetHelper.java中

// 初始化okhttpOkHttpClient client = new OkHttpClient.Builder()        .build();
/** *OkHttpClient每次請求的時候都要建立,注意:OkHttpClient.Builder()中有ConnectionPool作為OkHttp的 *連線池要複用,否則請求過多時容易導致記憶體溢位**/// 初始化Retrofitretrofit = new Retrofit.Builder() .client(client) .baseUrl(Request.HOST) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .addConverterFactory(MyConverterFactory.create()) .build();
複製程式碼

一般addConverterFactory配置是GsonConverterFactory,用於把Response通過GSon轉為javaBean(Request介面中定義的型別),由於我們定義為了JsonObject,所以我們使用MyConverterFactory自定義實現

第三步 生成Observable

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都獲取到了。

第四步 NetHelper Api的呼叫

以上三步,已經初步將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不同),方便架構使用者在事件響應時,既能捕捉所有錯誤,又能區分錯誤的型別。

框架的封裝性和Request cancle機制

框架的封裝性

看下最終使用的情況

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機制

Request的cancle機制,一般原理是這樣:在Activity/Fragment關閉時,需要cancle調正在請求中的網路,所以需要BaseActivity/BaseFragment的支援。方案有二,這篇文章就不贅述了,因為都是比較成熟的方案。

  1. 框架中加入RxLifecycle的繫結,這樣在呼叫NetHelper的Api時,要傳入BaseActivity/BaseFragment的例項
  2. 自己實現:在框架返回Observable的doOnSubscribe方法中,對當前request的disposable進行儲存,並設定tag儲存disposable和activity的對應關係,在BaseActivity的onDestroy方法中統一對disposable進行清理。

框架基於Rxjava的鏈式呼叫

我們常常遇到這樣一種場景,一個網路請求往往基於上一個網路請求的結果,或者一個頁面的資料來自兩個網路請求的組合,我們的網路框架是如何滿足這種需求的呢?這也是我們這套框架設計的出發點和落腳點:

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網路框架的解耦和複用會專門對消費者進行封裝,使該框架的規範性和擴充套件性更強。

來源:https://juejin.im/post/5c1b96f0e51d453b3b392044#comment

相關文章