RxJava(五)onErrorResumeNext 操作符实现 app 与服务器间 token 机制

RxJava 系列文章目录导读:

一、RxJava create 操作符的用法和源码分析
二、RxJava map 操作符用法详解
三、RxJava flatMap 操作符用法详解
四、RxJava concatMap 操作符用法详解
五、RxJava onErrorResumeNext 操作符实现 app 与服务器间 token 机制
六、RxJava retryWhen 操作符实现错误重试机制
七、RxJava 使用 debounce 操作符优化 app 搜索功能
八、RxJava concat 操作处理多数据源
九、RxJava zip 操作符在 Android 中的实际使用场景
十、RxJava switchIfEmpty 操作符实现 Android 检查本地缓存逻辑判断
十一、RxJava defer 操作符实现代码支持链式调用
十二、combineLatest 操作符的高级使用
十三、RxJava 导致 Fragment Activity 内存泄漏问题
十四、interval、takeWhile 操作符实现获取验证码功能
十五、RxJava 线程的自由切换


一、需求场景:

在开发 App 的时候, 很多公司的提 api 接口, 请求的的时候都需要带有 token, 该 token 在用户第一次启动 app 或者登陆的时候去获取. 以后的所有请求都需要带该 Token 如果 token 过期, 服务器将返回 401, 这时候就需要去请求获取 token 的接口, 如果获取成功接着在请求原来的接口. 这个时候就两个回调的嵌套了. 实现起来比较费劲, 而且也不够优雅. 代码的可维护性变得很差. 可以使用 onErrorResumeNext 来处理这样的业务逻辑.

例如:请求一个用户信息接口,如果 token 没有过期,返回用户信息,如果 token 过期,服务器返回 401,客户端发一个获取新 token 的请求,成功后,再去请求用户信息接口。

二、如何使用 onErrorResumeNext 解决

使用 Retrofit 来访问服务器 API


    private static RestAdapter restAdapter = new RestAdapter
            .Builder()
            .setLogLevel(RestAdapter.LogLevel.FULL)
            .setEndpoint(BASE_URL)
            .setErrorHandler(new NetWorkErrorHandler())
            .setRequestInterceptor(requestInterceptor)
            .build();


    public static <S> S createService(Class<S> serviceClazz) {
        return restAdapter.create(serviceClazz);
    }

如果服务器返回 401,我们要去请求新的 token,下面来判断错误类型:

NetWorkErrorHandler.java

private static class NetWorkErrorHandler implements ErrorHandler {
    @Override
    public Throwable handleError(RetrofitError error) {
        retrofit.client.Response r = error.getResponse();
        if (r != null && r.getStatus() == 401) {
            Log.e("ErrorHandler", "---------> access deny code=401");
            // User Custom Exception
            return new AccessDenyException(error.getMessage());
        }
        return error.getCause();
    }
}

UserApi.java

public interface UserApi {

    @GET("/token")
    AuthToken refreshToken();
}

服务器端代码逻辑

服务器端使用 Java web+Tomcat 来实现的. 如果需要可以把服务器部署在你的本地机器上, github地址
服务器端的基本逻辑:客户端请服务器api,服务器判断客户端带过来的 token,如果过期则返回 401,提示没有权限访问;如果是请求 token 接口,则返回 token,有效期为 10s。

客户端 App 的实现

以一个请求用户信息接口为例

Observable<Response> observable = userApi.getUserInfo();
        observable.onErrorResumeNext(refreshTokenAndRetry(observable))//also use retryWhen to implement it
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<Response>() {
                    @Override
                    public void onCompleted() {
                        loading = false;
                        appendText(tvLogs, "task completed-----");
                        //hideLoadingDialog();
                    }

                    @Override
                    public void onError(Throwable t) {
                        //hideLoadingDialog();
                        t.printStackTrace();
                        loading = false;
                        appendText(tvLogs, t.getClass().getName() + "\n" + t.getMessage());
                        NetErrorType.ErrorType error = NetErrorType.getErrorType(t);
                        appendText(tvLogs, error.msg);
                    }

                    public void onNext(Response response) {
                        String content = new String(((TypedByteArray) response.getBody()).getBytes());
                        appendText(tvLogs, "receiver data: " + content);
                    }
                });

核心代码


    private <T> Func1<Throwable, ? extends Observable<? extends T>> refreshTokenAndRetry(final Observable<T> toBeResumed) {
        return new Func1<Throwable, Observable<? extends T>>() {
            @Override
            public Observable<? extends T> call(Throwable throwable) {
                throwable.printStackTrace();
                // Here check if the error thrown really is a 401
                if (isHttp401Error(throwable)) {
                    return createTokenObvervable().flatMap(new Func1<AuthToken, Observable<? extends T>>() {
                        @Override
                        public Observable<? extends T> call(AuthToken token) {
                            appendText(tvLogs, "refresh token success,token's validity is 10s\nResume last request");
                            return toBeResumed;
                        }
                    });
                }
                // re-throw this error because it's not recoverable from here
                return Observable.error(throwable);
            }

            public boolean isHttp401Error(Throwable throwable) {
                return throwable instanceof AccessDenyException;
            }

        };
    }

请求token api 的Observable


    public Observable<AuthToken> createTokenObvervable() {
        return Observable.create(new Observable.OnSubscribe<AuthToken>() {
            @Override
            public void call(Subscriber<? super AuthToken> observer) {
                try {
                    if (!observer.isUnsubscribed()) {
                        appendText(tvLogs, "God!!! Token is out of date. \nstart refresh token......");
                        observer.onNext(userApi.refreshToken());
                        observer.onCompleted();
                    }
                } catch (Exception e) {
                    observer.onError(e);
                }
            }
        }).subscribeOn(Schedulers.io());
    }


运行效果:

这里写图片描述


如果你觉得本文帮助到你,给我个关注和赞呗!

另外,我为 Android 程序员编写了一份:超详细的 Android 程序员所需要的技术栈思维导图

如果有需要可以移步我的 GitHub -> AndroidAll,里面包含了最全的目录和对应知识点链接,帮你扫除 Android 知识点盲区。 由于篇幅原因只展示了 Android 思维导图:
超详细的Android技术栈

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 技术工厂 设计师:CSDN官方博客 返回首页
实付 39.90元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值