實際開發(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對象并進行后續(xù)操作椭住;如果status為FAILURE,則需要根據(jù)code或者message提示用戶相應(yīng)的錯誤提示字逗,并隱藏進度條對話框等操作京郑。
下面有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進行不同處理
...
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();
}
});
顯然這個方案有太多缺點:
1傻挂、RxJava建議在subscribe的時候,觀察者應(yīng)該拿到的是加工完成的數(shù)據(jù)源挖息,而不是未加工的數(shù)據(jù)源;
2兽肤、在服務(wù)器返回status=FAILURE的情況下套腹,異常是在onNext里處理,這樣并不合適资铡;
3电禀、判斷RESTResult的部分更應(yīng)該放在鏈式操作里。
我們需要炫酷的Rx操作符笤休!
最佳體驗:
方案2:使用Observable.error(e):
_apiService.login(mobile, verifyCode)
.// 略 顯示提示框尖飞、切換線程
.flatMap(result -> {
if (result.getStatus() == RESTResult.FAILURE) {
HttpResponseCode code = result.getCode();
// 根據(jù)不同code進行不同處理
...
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,當觀察者訂閱時窍奋,執(zhí)行觀察者的onError方法荐健。
這個方案完美的解決了方案1酱畅、2的問題:
1、觀察者拿到“加工”完成的數(shù)據(jù)源江场;
2纺酸、異常方面,不管是服務(wù)器異常還是其他異常情況址否,最終觀察者都可以接收到異常數(shù)據(jù)源吁峻,并最終統(tǒng)一在onError里處理。
3在张、使用flatMap處理RESTResult部分的代碼用含,可以放在任意線程處理
最后:
方案2通過合適的封裝,都可達到代碼簡化的目的帮匾。
封裝部分可以查看我這篇簡書:RxJava簡潔封裝之道
通過RxJava的鏈式操作啄骇,結(jié)合恰當?shù)牟僮鞣粌H可以把正常的數(shù)據(jù)源發(fā)射給觀察者瘟斜,同時也可以將錯誤異常數(shù)據(jù)源發(fā)射給觀察者缸夹,RxJava遠比想象中的強大!
方案2是目前我發(fā)現(xiàn)的最合適方案螺句,如果還有更好的解決方案虽惭,歡迎告訴我哈~