使用RxJava的retryWhen操作符实现token过期自动刷新

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的时候我也是找了一大堆文章,虽然没有完全吻合的但是最后摸索摸索也就做了出来。上面的代码在我的项目中是进行过封装的,这里为了演示所以把封装的东西全部省去了。

学习无止境,自己的语言表达和组织能力还是很弱,必须要加把劲了。

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值