基于RxJava Retrofit網絡框架的搭建
RxJava、Retrofit兩個第三方庫的優勢
RxJava的使用場景
本文討論的如無特殊說明,均指代rxjava2 和 retrofit2。
我們先討論一下rxJava引入的背景。有一些應用場景,特別是一些復雜的異步回調場景,如果使用傳統的開發模式,會如何實現。
列舉一些場景:
- EditText: 根據輸入內容自動搜索時,只有當輸入間隔大于某個duration時,才觸發搜索。
同時要根據輸入內容,實時判斷其有效性。
對輸入的內容,實時做出改變(比如不能輸入數字,特殊字符等)
(http://www.lxweimin.com/p/9aaccd7bb600) - 聯合判斷
http://www.lxweimin.com/p/88ce90240396 - 注冊--登錄--獲取用戶信息(注冊的一般流程) 獲取權限--獲取經緯度--鑒權--生成訂單(生成訂單一般流程) 這樣一連串的網絡請求。
- 結合多個接口的數據,再更新UI;或者歷史記錄、購物車記錄等,需要合并本地緩存和網絡請求返回的數據;
- 網絡請求前置條件token的獲取,token可以本地緩存,本地緩存拿不到要去網絡獲取。同時對于token拿不到時的網絡請求先等待,token獲取后將等待的請求一一發出。甚至token請求需要設置重試次數,超過次數才停止請求
- 防重復操作,倒計時
- 多重緩存處理:先讀取緩存更新UI,在網絡請求更新UI(http://www.lxweimin.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。