實際開發(fā)經(jīng)常有這種情況,比如登錄請求,返回來的并不會僅僅是User對象,而是被包裝的RESTResult<User>對象,RESTResult對象里,包括請求返回的狀態(tài):失敗還是成功,錯誤碼,User對象等等。
如下:
public class RESTResult<T> {
public static final int FAILURE = 0; // 失敗
public static final int SUCCESS = 1; // 成功
private int status; // 返回狀態(tài):0 失敗 1 成功
private HttpResponseCode code; // 錯誤碼
private String message; // 返回信息
private T data; // 包裝的對象
// 其他省略
....
在使用Retrofit+RxJava的情況下,以登錄為例,ApiService如下:
public interface ApiService {
@FormUrlEncoded
@POST(API + "account/login")
Observable<RESTResult<User>> login(@Field("mobile") String mobile, @Field("code") String code);
}
我們對于登錄返回的結(jié)果會這樣處理:如果status為SUCCESS,則獲取到User對象并進(jìn)行后續(xù)操作;如果status為FAILURE,則需要根據(jù)code或者message提示用戶相應(yīng)的錯誤提示,并隱藏進(jìn)度條對話框等操作。
下面有2種處理方式,方案1是我們沒接觸或剛接觸Rx時會想到的方案,方案2里是比較優(yōu)雅的處理方式 :)
方案1:
_apiService.login(mobile, verifyCode)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnTerminate(() -> hideLoadingDialog())
.subscribe(new Action1<RESTResult<User>>() {
@Override
public void call(RESTResult<User> userRESTResult) {
if (result.getStatus() == RESTResult.FAILURE) {
HttpResponseCode code = result.getCode();
// 根據(jù)不同code進(jìn)行不同處理
...
Toast.makeText(_context, result.getMessage(), Toast.LENGTH_SHORT).show();
} else {
User user = result.getData();
...
}
}
}, new Action1<Throwable>() {
@Override
public void call(Throwable throwable) {
throwable.printStackTrace();
Toast.makeText(_context, "請求失敗,請稍后重試", Toast.LENGTH_SHORT).show();
}
});
顯然這個方案有太多缺點(diǎn):
1、RxJava建議在subscribe的時候,觀察者應(yīng)該拿到的是加工完成的數(shù)據(jù)源,而不是未加工的數(shù)據(jù)源;
2、在服務(wù)器返回status=FAILURE的情況下,異常是在onNext里處理,這樣并不合適;
3、判斷RESTResult的部分更應(yīng)該放在鏈?zhǔn)讲僮骼铩?br>
我們需要炫酷的Rx操作符!
最佳體驗:
方案2:使用Observable.error(e):
_apiService.login(mobile, verifyCode)
.// 略 顯示提示框、切換線程
.flatMap(result -> {
if (result.getStatus() == RESTResult.FAILURE) {
HttpResponseCode code = result.getCode();
// 根據(jù)不同code進(jìn)行不同處理
...
return Observable.error(new ServerException(result.getMessage()));
}
return Observable.just(result.getData());
})
.subscribe(new Action1<User>() {
@Override
public void call(User user) {
// user對象
}
}, new Action1<Throwable>() {
@Override
public void call(Throwable throwable) {
throwable.printStackTrace();
if (e instanceof ServerException){
Toast.makeText(_context, e.getMessage(), Toast.LENGTH_SHORT).show();
} else{
Toast.makeText(_context, "請求失敗,請稍后重試", Toast.LENGTH_SHORT).show();
}
}
});
flaMap操作符可以接收一個Observable的輸出作為輸入,同時輸出另外一個Observable。在服務(wù)器返回status=FAILURE的情況下,返回Observable.error(Throwable exception),SUCCESS的話,just源數(shù)據(jù)為一個新的Observable返回。
Observable.error(Throwable exception):
Returns an Observable that invokes an Observer's onError method when the Observer subscribes to it.
該方法是返回一個Observable,當(dāng)觀察者訂閱時,執(zhí)行觀察者的onError方法。
這個方案完美的解決了方案1、2的問題:
1、觀察者拿到“加工”完成的數(shù)據(jù)源;
2、異常方面,不管是服務(wù)器異常還是其他異常情況,最終觀察者都可以接收到異常數(shù)據(jù)源,并最終統(tǒng)一在onError里處理。
3、使用flatMap處理RESTResult部分的代碼,可以放在任意線程處理
最后:
方案2通過合適的封裝,都可達(dá)到代碼簡化的目的。
封裝部分可以查看我這篇簡書:RxJava簡潔封裝之道
通過RxJava的鏈?zhǔn)讲僮?,結(jié)合恰當(dāng)?shù)牟僮鞣粌H可以把正常的數(shù)據(jù)源發(fā)射給觀察者,同時也可以將錯誤異常數(shù)據(jù)源發(fā)射給觀察者,RxJava遠(yuǎn)比想象中的強(qiáng)大!
方案2是目前我發(fā)現(xiàn)的最合適方案,如果還有更好的解決方案,歡迎告訴我哈~