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的时候我也是找了一大堆文章,虽然没有完全吻合的但是最后摸索摸索也就做了出来。上面的代码在我的项目中是进行过封装的,这里为了演示所以把封装的东西全部省去了。
学习无止境,自己的语言表达和组织能力还是很弱,必须要加把劲了。