使用RxJava的retryWhen操作符實現token過期自動重新整理
阿新 • • 發佈:2019-02-02
1.問題描述
我們的專案中請求與登入相關介面時需要帶上sessionId這個引數,當發現token過期的時候就需要走重新整理token的介面,獲取最新的token,然後再重新進行請求。
如果專案中是用OkHttp網路框架的話,那麼可以使用Authenticator或者Interceptor來實現,可以參考這篇文章 http://www.jianshu.com/p/62ab11ddacc8。我剛開始的時候也是使用的Interceptor實現的,攔截請求->檢測是否過期->過期則更新token->重新發送請求,但是我發現我的介面回撥中的response仍然是token更新之前的!我已經確保了新的請求攜帶的是最新的token了啊,折騰了好久都沒搞定,所以最後就使用了retryWhen來實現。
2.說明
首先要說明我們專案中資料的封裝方式
返回的json格式
{
"errorCode" : ...
"errorMessage" : ...
....
}
errorCode和errorMessage是所有請求都會返回的欄位,我們就是根據errorCode的值來判斷token是否過期,當然你也可能是用狀態碼或者其它方式,差別只是判斷token過期的方法不同而已。
所以我們的封裝方式就是有一個MobileResponse基類,包含errorCode和errorMessage兩個欄位,然後具體的請求繼承MobileResponse。
public class MobileResponse {
private String errorCode;
private String errorMessage;
....
}
3.程式碼實現
Api介面
// 需要使用token的請求 @POST("updateMobile") Observable<MobileResponse> updateMobile(@Body UpdateMobileRequest updateMobileRequest); // 重新整理token的請求 @POST("login") Observable<LoginResponse> login(@Body LoginRequest request);
呼叫
public void updateMobile(Observer<MobileResponse> observer, UpdateMobileRequest updateMobileRequest) {
Observable.just(null)
// 這裡呼叫flatMap方法,主要是需要在這裡設定token,這樣當更新了token之後再次訂閱時,token也是最新的了
.flatMap(new Function<Object, Observable<MobileResponse>>() {
@Override
public Observable<MobileResponse> apply(Object object) throws Exception {
// 設定token(我這裡token,session意思是一樣的)
updateMobileRequest.setSessionId(UserInfo.getInstance().getSession());
// apiService是我上面Api介面的一個例項
return apiService.updateMobile(updateMobileRequest);
}
})
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.flatMap(new Function<MobileResponse, Observable<? extends MobileResponse>>() {
@Override
public Observable<? extends MobileResponse> apply(@NonNull MobileResponse response) throws Exception {
// 判斷token是否過期
if (ErrorCode.ERROR_NOAUTH.equals(response.getErrorCode())) {
return Observable.error(new TokenExpiredException());
}
return Observable.just(response);
}
})
.retryWhen(new Function<Observable<Throwable>, ObservableSource<?>>() {
@Override
public ObservableSource<?> apply(@NonNull Observable<Throwable> throwableObservable) throws Exception {
return throwableObservable.flatMap(new Function<Throwable, ObservableSource<?>>() {
@Override
public ObservableSource<?> apply(@NonNull Throwable throwable) throws Exception {
if (throwable instanceof TokenExpiredException) {
// 如果上面檢測到token過期就會進入到這裡
// 然後下面的方法就是更新token
return apiService.login(HttpMethods.this.buildLoginRequest())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.unsubscribeOn(Schedulers.io())
.doOnNext(loginResponse -> {
// 儲存最新的token
// 這裡更新完成後就會進行重訂閱,從Observable.just(null)重新開始走。
UserInfo.getInstance().setSession(AndroidApplication.getInstance(), loginResponse.getSessionId());
});
}
// 如果是其他錯誤則會呼叫到observer的onError方法中
return Observable.error(throwable);
}
});
}
})
.subscribe(observer);
}
Observer<MobileResponse> observer = new Observer<MobileResponse>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
}
@Override
public void onNext(@NonNull MobileResponse response) {
}
@Override
public void onError(@NonNull Throwable e) {
}
@Override
public void onComplete() {
}
};
UpdateMobileRequest request = new UpdateMobileRequest();
設定引數...
updateMobile(observer, request);
TokenExpiredException
public class TokenExpiredException extends Exception {
// 什麼都不用做,當然上面使用TokenExpiredException的地方也可以用其它不會與網路請求產生的異常衝突的異常
}
4.總結
如果資料封裝形式不同的話一些地方就需要改動,這裡也沒有加重試次數的限制。在使用retryWhen的時候我也是找了一大堆文章,雖然沒有完全吻合的但是最後摸索摸索也就做了出來。上面的程式碼在我的專案中是進行過封裝的,這裡為了演示所以把封裝的東西全部省去了。
學習無止境,自己的語言表達和組織能力還是很弱,必須要加把勁了。