基于RxJava Retrofit網(wǎng)絡(luò)框架的搭建
RxJava、Retrofit兩個(gè)第三方庫的優(yōu)勢(shì)
RxJava的使用場(chǎng)景
本文討論的如無特殊說明遏考,均指代rxjava2 和 retrofit2死相。
我們先討論一下rxJava引入的背景杨名。有一些應(yīng)用場(chǎng)景,特別是一些復(fù)雜的異步回調(diào)場(chǎng)景常潮,如果使用傳統(tǒng)的開發(fā)模式斥黑,會(huì)如何實(shí)現(xiàn)揖盘。
列舉一些場(chǎng)景:
- EditText: 根據(jù)輸入內(nèi)容自動(dòng)搜索時(shí),只有當(dāng)輸入間隔大于某個(gè)duration時(shí)锌奴,才觸發(fā)搜索兽狭。
同時(shí)要根據(jù)輸入內(nèi)容,實(shí)時(shí)判斷其有效性鹿蜀。
對(duì)輸入的內(nèi)容箕慧,實(shí)時(shí)做出改變(比如不能輸入數(shù)字,特殊字符等)
(http://www.reibang.com/p/9aaccd7bb600) - 聯(lián)合判斷
http://www.reibang.com/p/88ce90240396 - 注冊(cè)--登錄--獲取用戶信息(注冊(cè)的一般流程) 獲取權(quán)限--獲取經(jīng)緯度--鑒權(quán)--生成訂單(生成訂單一般流程) 這樣一連串的網(wǎng)絡(luò)請(qǐng)求茴恰。
- 結(jié)合多個(gè)接口的數(shù)據(jù)颠焦,再更新UI;或者歷史記錄往枣、購(gòu)物車記錄等伐庭,需要合并本地緩存和網(wǎng)絡(luò)請(qǐng)求返回的數(shù)據(jù);
- 網(wǎng)絡(luò)請(qǐng)求前置條件token的獲取,token可以本地緩存分冈,本地緩存拿不到要去網(wǎng)絡(luò)獲取圾另。同時(shí)對(duì)于token拿不到時(shí)的網(wǎng)絡(luò)請(qǐng)求先等待,token獲取后將等待的請(qǐng)求一一發(fā)出丈秩。甚至token請(qǐng)求需要設(shè)置重試次數(shù)盯捌,超過次數(shù)才停止請(qǐng)求
- 防重復(fù)操作,倒計(jì)時(shí)
- 多重緩存處理:先讀取緩存更新UI蘑秽,在網(wǎng)絡(luò)請(qǐng)求更新UI(http://www.reibang.com/p/7474950af2df)
- 多處場(chǎng)景需要同一個(gè)監(jiān)聽事件饺著,但是每個(gè)場(chǎng)景注冊(cè)時(shí)間點(diǎn)不同,如何做到事件產(chǎn)生之后注冊(cè)的監(jiān)聽者也能收到回調(diào)肠牲。
這些應(yīng)用場(chǎng)景幾乎是每個(gè)app都會(huì)遇到的幼衰,傳統(tǒng)的處理方式:層層遞進(jìn)(回調(diào)),各種標(biāo)志位控制缀雳,嵌套的if else渡嚣,各種異步調(diào)用分散在代碼各處,要將各個(gè)回調(diào)結(jié)果匯總肥印,并且綜合處理回調(diào)的不同狀態(tài)识椰。而且由于異步回調(diào)的特點(diǎn)深碱,代碼處理方式不會(huì)像同步調(diào)用那樣直接可控,異步回調(diào)導(dǎo)致的時(shí)序問題又將問題復(fù)雜度提高了幾個(gè)level敷硅。我經(jīng)常會(huì)想功咒,這種callback的異步調(diào)用方式,為何會(huì)將代碼變得丑陋不堪和難以維護(hù)力奋?設(shè)計(jì)模式中只提供了觀察者模式,但是對(duì)于觀察者回調(diào)復(fù)雜后景殷,沒有提出更好的解決方案。RxJava(或者說響應(yīng)式編程)主要解決了這類的問題滨彻。
關(guān)于RxJava是如何解決以上場(chǎng)景中遇到的問題藕届,以及如何和retrofit一起聯(lián)合使用休偶,短短幾行無法說不清,在基于RxJava Retrofit的網(wǎng)絡(luò)框架(二)中會(huì)詳解辜羊。
Retrofit的使用
Retrofit并不實(shí)現(xiàn)網(wǎng)絡(luò)請(qǐng)求本身(網(wǎng)絡(luò)請(qǐng)求由okhttp負(fù)責(zé)),他是一個(gè)框架八秃,為網(wǎng)絡(luò)請(qǐng)求本身提供可擴(kuò)展,可配置昔驱,易使用的外部封裝疹尾。okhttp負(fù)責(zé)提高網(wǎng)絡(luò)請(qǐng)求的性能和兼容性,Retrofit負(fù)責(zé)更好的使用他骤肛∧杀荆框架的重要性不言而喻,好的框架讓使用者關(guān)注更少細(xì)節(jié)腋颠,輕易的擴(kuò)展繁成。okhttp好像汽車的發(fā)動(dòng)機(jī),對(duì)于汽車性能和穩(wěn)定性起到?jīng)Q定性作用淑玫,框架就是除了發(fā)動(dòng)機(jī)以外其他部分巾腕,除了對(duì)發(fā)送機(jī)功能的整合外,漂亮的外觀絮蒿,舒適易用的體驗(yàn)才是人們?cè)敢忾_這臺(tái)車的原因尊搬。
以下列舉Retrofit相對(duì)于volley Android-Async-Http等框架的優(yōu)勢(shì)
- 簡(jiǎn)潔易用:通過注解配置網(wǎng)絡(luò)參數(shù),大量設(shè)計(jì)模式(建造者模式土涝,工廠)簡(jiǎn)化配置和使用毁嗦。
- 功能強(qiáng)大:支持RxJava方式(也支持callback方式),支持同步&異步回铛,
- 耦合度低狗准,擴(kuò)展性好:模塊高度封裝,徹底解耦茵肃。
由于Android-Async-Http的停更腔长,Google對(duì)volley基本放棄的態(tài)度,這兩套框架使用者和價(jià)值日漸減少验残。反觀okhttp捞附,Google官方應(yīng)用則廣泛使用。作為okhttp的黃金搭檔(同為square公司出品)您没,Retrofit可以說是目前Android app網(wǎng)絡(luò)請(qǐng)求框架的不二選擇鸟召,與okhttp搭配,是性能氨鹏、穩(wěn)定性欧募、兼容性、易用性都達(dá)到很高水平的框架組合仆抵。
Observable網(wǎng)絡(luò)框架的抽象
Observable網(wǎng)絡(luò)框架建立的原因
- Retrofit已經(jīng)對(duì)網(wǎng)絡(luò)請(qǐng)求做了封裝跟继,為什么還要封裝?
網(wǎng)絡(luò)請(qǐng)求中對(duì)于請(qǐng)求流程、配置镣丑、加解密舔糖、異常處理對(duì)于每個(gè)app都是固定不變的,如果業(yè)務(wù)每次請(qǐng)求都自己處理邏輯莺匠,會(huì)存在冗余代碼秸脱,且質(zhì)量不易保證。所以我們需要基于Retrofit對(duì)請(qǐng)求流程侨赡、配置胆剧、加解密、異常處理等操作做二次封裝期贫,并對(duì)調(diào)用方式進(jìn)行統(tǒng)一。 - 框架封裝方式Observable是什么玛臂?
對(duì)網(wǎng)絡(luò)請(qǐng)求二次封裝(一般為異步請(qǐng)求)封孙,傳統(tǒng)使用callback方式異步回調(diào)網(wǎng)絡(luò)請(qǐng)求結(jié)果。但是這種callback的方式泡徙,沒有利用到Retrofit的一大優(yōu)勢(shì)--rxjava調(diào)用膜蠢,所以我們要基于rxjava調(diào)用方式莉兰,封裝一個(gè)基于Observable的網(wǎng)絡(luò)請(qǐng)求框架糖荒。
以下所說網(wǎng)絡(luò)框架模捂,均指基于Observable的網(wǎng)絡(luò)請(qǐng)求二次封裝框架。
Observable網(wǎng)絡(luò)框架要解決的問題
網(wǎng)絡(luò)框架要幫助業(yè)務(wù)處理以下幾個(gè)問題:
- 支持Get Post請(qǐng)求综看,對(duì)Request的參數(shù)業(yè)務(wù)可輕松配置
- 對(duì)Request 參數(shù)做發(fā)送前處理:組合和加密處理
- 返回Response 解密處理岖食,Java實(shí)體化
- 返回Response code碼判斷及處理
- 網(wǎng)絡(luò)請(qǐng)求cancle機(jī)制 progressBar配置等通用處理
達(dá)到的目標(biāo):業(yè)務(wù)使用框架時(shí),只需要關(guān)注業(yè)務(wù)相關(guān)(Request參數(shù)句喷,Response返回值的配置和處理)兔毙,其他都交給框架處理。同時(shí)對(duì)于網(wǎng)絡(luò)請(qǐng)求的屬性可配置(error是否提示锡溯,progressBar是否顯示等)
Observable網(wǎng)絡(luò)框架如何實(shí)現(xiàn)
設(shè)計(jì)原則:
- 網(wǎng)絡(luò)請(qǐng)求Api返回Observable對(duì)象哑姚,作為網(wǎng)絡(luò)請(qǐng)求事件的生產(chǎn)者:
生產(chǎn)者負(fù)責(zé)請(qǐng)求的發(fā)起叙量,和返回的所有預(yù)處理。 - 為業(yè)務(wù)提供BaseObserver類寺鸥,使用者實(shí)現(xiàn)其子類作為消費(fèi)者
消費(fèi)者基類提供對(duì)response的一般處理品山,消費(fèi)者業(yè)務(wù)也可以使用自己Observer處理
在Observable的創(chuàng)建過程中,框架如何封裝笆载?
首先我們需要一個(gè)Manager或Helper全局實(shí)例,通過他可以發(fā)起網(wǎng)絡(luò)請(qǐng)求腻要,一般設(shè)計(jì)為單例全局持有沿侈,有利于網(wǎng)絡(luò)請(qǐng)求一些資源的共用。
我們暫定為NetHelper,其網(wǎng)絡(luò)請(qǐng)求接口定義為:
private static Observable<R> sendRequest(final HttpRequest request, final TypeReference<R> t)
sendRequest方法中,我們來看下Observable對(duì)象的生成過程:此處我們基于Retrofit本身Observable生成方式填帽,我們先看下Retrofit最基礎(chǔ)是如何創(chuàng)建Observable的。
第一步褐荷,定義Request嘹悼,Request類的定義在Retrofit里通過注解的方式完成的
public interface Request {
@POST("{url}")
Observable<JSONObject> postJSONResult(@Path(value="url",encoded =true) String url, @FieldMap Map<String, String> params);
}
可以看到我們的定義方式是通用的(每個(gè)request都可以復(fù)用),每個(gè)request都是通過postJSONResult方法獲取observable其监,傳入自己的url和params即可完成不同的網(wǎng)絡(luò)請(qǐng)求抖苦。
第二步,創(chuàng)建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每次請(qǐng)求的時(shí)候都要?jiǎng)?chuàng)建锌历,注意:OkHttpClient.Builder()中有ConnectionPool作為OkHttp的連接池要復(fù)用究西,否則請(qǐng)求過多時(shí)容易導(dǎo)致內(nèi)存溢出物喷。
創(chuàng)建Retrofit實(shí)例過程中,設(shè)置了okHttpClient商膊,baseUrl宠进,調(diào)用方式rxJava(通過addCallAdapterFactory)
GsonConverterFactory這些都是一般的寫法,GsonConverterFactory作用是把Response通過GSon轉(zhuǎn)為javaBean实幕。App業(yè)務(wù)中一般是先解密后Gson轉(zhuǎn),所以此處使用MyConverterFactory實(shí)現(xiàn)解密功能末贾。
第三步整吆,生成Observable
這一步是生成observable的過程,與httpRequest本身有關(guān)(我們前面提到了Request類是一個(gè)支持Retrofit通用類拴测,業(yè)務(wù)自定義的請(qǐng)求類HttpRequest實(shí)現(xiàn)了 getURLParam() getURLAction()等方法),所以這個(gè)獲取Observable的方法可以放到HttpRequest中進(jìn)行(NetHelper.sendRequest方法是傳入了HttpRequest對(duì)象的)
Request request = retrofit.create(Request.class);
return request.postJSONResult(getURLAction(),getURLParam());
對(duì)于httpRequest中入?yún)⒌慕M合和加密府蛇,實(shí)現(xiàn)在getURLParam()方法里。
備注:我們的網(wǎng)絡(luò)post請(qǐng)求params是query形式的务荆,如果是body表單穷遂,還需要另外的處理方式。
以上三步浦箱,已經(jīng)初步將Observable返回祠锣。通過以上幾步只是基于Retrofit自身的Observable創(chuàng)建方法做了一些封裝。下面的處理是框架的重點(diǎn)和核心:
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所有的鏈?zhǔn)讲僮鲌?zhí)行完后在返回澡腾。
NetHelper.getApiObservable方法后,再加上網(wǎng)絡(luò)請(qǐng)求的線程配置毅糟,這時(shí)候業(yè)務(wù)subscribe消費(fèi)者澜公,就可以直接得到解密后的JsonObject了。注意此時(shí)是string而不是Retrofit通常的JavaBean迹辐,這是因?yàn)槲覀円x一個(gè)通用的Request類,將其接口返回值定義為了Observable<JSONObject>,所以我們還需要一步轉(zhuǎn)換间学。
.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的顯示等仍律,也需要架構(gòu)統(tǒng)一處理。我們引入了ResponseTransformer,你可以把他理解為map操作符主到,在交給消費(fèi)者前對(duì)response結(jié)果做了處理。
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));
}
}
}
可以看出對(duì)于事件流upstream登钥,做了正常和異常的再分流,對(duì)于服務(wù)器錯(cuò)誤(超時(shí)看锉,404等)通過onErrorResumeFunction繼續(xù)拋出Observable.error,
對(duì)于正常返回塔鳍,根據(jù)response中code的定義,只有SUCCESS時(shí)才返回?cái)?shù)據(jù)Observable.just(),其他情況(業(yè)務(wù)錯(cuò)誤等)作為錯(cuò)誤情況繼續(xù)拋出轮纫。
你可能有兩個(gè)疑問,一個(gè)是response中code的判定可以在observer中處理嗎放前,另一個(gè)是服務(wù)器錯(cuò)誤和業(yè)務(wù)錯(cuò)誤為何都作為error拋出糯彬。
第一個(gè)問題:
code值的判定不可以在observer中處理,而必須在Observable一端處理似扔。因?yàn)镺bservable形式的網(wǎng)絡(luò)請(qǐng)求是作為數(shù)據(jù)流中的一環(huán)出現(xiàn)的,可能當(dāng)前網(wǎng)絡(luò)請(qǐng)求只是一連串異步調(diào)用(rxjava調(diào)用)的一環(huán)锤灿。
第二個(gè)問題:
response中code!=SUCCESS是業(yè)務(wù)錯(cuò)誤的情況辆脸,必須向數(shù)據(jù)流中發(fā)出,讓業(yè)務(wù)處理此異常状囱。(那同時(shí)對(duì)于Response的定義也是倘是,code!=SUCCESS必須是不需要業(yè)務(wù)處理的情況才行)
兩種錯(cuò)誤都拋出error(內(nèi)部code不同),方便架構(gòu)使用者在事件響應(yīng)時(shí)叨粘,既能捕捉所有錯(cuò)誤瘤睹,又能區(qū)分錯(cuò)誤的類型。
哪些處理放到了BaseObserver中?
BaseObserver顧名思義驴党,是架構(gòu)使用者在rxjava流式調(diào)用最后一步所使用的觀察者基類获茬,他適合將網(wǎng)絡(luò)請(qǐng)求的UI響應(yīng)放入其中。
public abstract class BaseObserver<T > implements Observer<T>{
/**
* 請(qǐng)求成功
* @param t
*/
public abstract void onSuccess(T t);
/**
* 請(qǐng)求失敗
* @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;
}
}
}
/**
* 網(wǎng)絡(luò)請(qǐng)求是否loading顯示
* @return
*/
protected boolean isShowProgress(){
return true;
}
}
BaseObserver中鹏氧,我們可以看到處理了showProgress度帮,ApiException做了處理稿存。同時(shí)可以對(duì)progressBar是否顯示做配置÷食幔可以滿足一般的網(wǎng)絡(luò)請(qǐng)求架構(gòu)使用袖迎,當(dāng)然也可以自行subscribe自己的Observer腺晾。