巧用RxJava解决网络连接失败问题及Token失效自动获取问题

网络连接失败的处理

看过最前面那篇文章的应该很清楚retryWhen()是什么了。
我再来总结一下,retryWhen()的直面意思就是:发生错误了,接下来该做什么。
retryWhen()是RxJava的一种错误处理机制,当遇到错误时,将错误传递给另一个Observable来决定是否要重新给订阅这个Observable

延迟重试
来想象一个场景:用户用的2G网络或者WiFi信号不稳定,导致网络经常连接失败,其实这个时候只要多努力一下就可以连接成功了,如果此时弹出错误提示,体验肯定不好,所以这里就要用到重试机制

判断错误类型
再模拟一个场景:用户不是手机信号不好,而是根本就没打开网络,此时还傻傻的重试只是浪费电量而已,所以我们可以加一个判断,打开了网络才重试,没有打开网络就继续发送失败的消息。

加入重试超时
继续想,重试也不可能永远进行,一般都会设置一个重试超时的机制。
想一下,没有哪个APP只有一个接口地址吧 - -#,如果你用的Retrofit那么每一个接口返回的Observable都要手动加上上面的重试代码,如果是我,我肯定报警了……所以我们必须把刚刚写的重试代码封装成一个类:

public class TryWhenTransaction implements Func1<Observableextends Throwable>, Observable> {

    private static final String TAG = "TokenAdvancedFragment";
    /***
     * 重试间隔时间
     */
    private long mInterval;

    public TryWhenTransaction(long interval) {
        mInterval = interval;
    }

    private int retryCount = 0;

    @Override
    public Observable> call(final Observable extends Throwable> observable) {
        return observable.flatMap(new Func1>>() {
            @Override
            public Observable> call(Throwable throwable) {

                if (throwable instanceof UnknownHostException) {
                    //若没打开网络则停止重试
                    return Observable.error(throwable);
                } else if (throwable instanceof NullPointerException)
                    Log.i(TAG, "call: Time:" + new Date(System.currentTimeMillis()) + " thread:" + Thread.currentThread().getName());
                //重试三次
                if (++retryCount < 3)
                    return Observable.timer(5, TimeUnit.SECONDS);
                else
                    return Observable.error(new IllegalArgumentException("超过最大次数"));//超过最大次数终止
            }
        });
    }
}

调用代码:

public void testMethodA() {
        Observable.just(token).flatMap(new Func1>() {
            @Override
            public Observable call(Integer o) {
                //若请求数据得不到响应 则返回一个Error 会激活retry
                Log.i(TAG, "call: first  token invalide is:" + o + " thread:" + Thread.currentThread().getName());
                return Observable.error(new NullPointerException("请求数据失败"));
            }
        }).retryWhen(
                new TryWhenTransaction(5))
                .subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Subscriber() {
            @Override
            public void onCompleted() {
                Log.i(TAG, "onCompleted: ");
            }

            @Override
            public void onError(Throwable e) {
                //这边可以根据错误类型来分别处理
                if (e instanceof IllegalArgumentException) {
                    Log.e(TAG, "onError: ", e);
                }
            }

            @Override
            public void onNext(FakeThing fakeThing) {

            }
        });
    }

日志:
巧用RxJava解决网络连接失败问题及Token失效自动获取问题_第1张图片

token失效解决方案
用户长时间不访问app可能导致token的失效,这时如果继续访问app可能会导致用户重新登录降低体验,所以token失效自动重新获取可以增强用户体验。
上代码:

public void testMethodB() {
        // 使用token进行网络请求
        Observable.just(token).flatMap(new Func1>() {
            @Override
            public Observable call(Integer o) {
                //若token是1000则为无效token
                if (o == 1000) {
                    Log.i(TAG, "call: first  token invalide is:" + o);
                    return Observable.error(new RuntimeException("TokenError"));
                }
                Log.i(TAG, "call: after retry request token:" + token + "  original o is still:" + o);
                return Observable.just(new FakeThing());
            }
        }).retryWhen(
                new Func1, Observable>() {
                    @Override
                    public Observable call(final Observable observable) {
                        return observable.flatMap(new Func1>() {
                            @Override
                            public Observable call(Throwable throwable) {
                                if (throwable instanceof RuntimeException) {//如果是token错误就开启重试
                                    //重新请求Token 获取到token为2000
                                    token = 2000;
                                    Log.i(TAG, "call: retry execute token changed is:" + token);
                                    return observable.just(null).delay(1, TimeUnit.SECONDS);
                                }
                                return Observable.error(throwable);//其他错误就不开启重试
                            }
                        });
                    }
                })
                .subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Subscriber() {
            @Override
            public void onCompleted() {
                Log.i(TAG, "onCompleted: ");
            }

            @Override
            public void onError(Throwable e) {
                try {
                    throw e;
                } catch (Throwable throwable) {
                    throwable.printStackTrace();
                }
            }

            @Override
            public void onNext(FakeThing fakeThing) {

            }
        });
    }

上运行截图:巧用RxJava解决网络连接失败问题及Token失效自动获取问题_第2张图片
有木有发现新获取的Token本来应该是2000再重订阅的时候显示却是1000!其实,之所以造成这个的原因是因为对Obserable的工作机制没有理解透彻导致的。在创建一个Obserable的时候,参数的内容,固定的步骤就已经决定好了。 即使在过程中参数发生了改变,retry的时候,还是使用原来的值去请求的。可以使用HttpLoggingInterceptor验证这个。
那总是原始值怎么办?看下面
我们需要每次重试的时候,产生一个新的Obserable。这时候我们可以借助defer操作符来实现,每次调用都是一个新的Obserable。
把请求接口的Obserable用Obserable.defer包裹一下即可。

 public void testMethodC() {
        //使用defer重新包裹请求 可以在retry后使用新(重新获取的)token进行请求
        // 如果不包裹则会使用第一次传进去的token进行请求
        Observable.defer(new Func0>() {
            @Override
            public Observable call() {
                //这里写请求 由于token过期或者为null传回错误码-1

                return Observable.just(token);
            }
        }).flatMap(new Func1>() {
            @Override
            public Observable call(Integer o) {
                if (o == 1000) {
                    Log.i(TAG, "call: first  token invalide is:" + o);
                    return Observable.error(new RuntimeException("TokenError"));
                }
                Log.i(TAG, "call: after retry request token:" + token + "  original o is still:" + o);
                return Observable.just(new FakeThing());
            }
        }).retryWhen(
                new Func1, Observable>() {
                    @Override
                    public Observable call(final Observable observable) {
                        return observable.flatMap(new Func1>() {
                            @Override
                            public Observable call(Throwable throwable) {
                                if (throwable instanceof RuntimeException) {
                                    //重新请求Token 获取到token为2000
                                    token = 2000;
                                    Log.i(TAG, "call: retry execute token changed is:" + token);
                                    return observable.just(null);
                                }

                                return Observable.error(throwable);
                            }
                        });

                    }
                })
                .subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Subscriber() {
            @Override
            public void onCompleted() {
                Log.i(TAG, "onCompleted: ");
            }

            @Override
            public void onError(Throwable e) {
                try {
                    throw e;
                } catch (Throwable throwable) {
                    throwable.printStackTrace();
                }
            }

            @Override
            public void onNext(FakeThing fakeThing) {

            }
        });
    }

上截图:
这里写图片描述

至于次数限制就自己加仿造本片第一段代码。

你可能感兴趣的:(Web)