巧用RxJava解决网络连接失败问题及Token失效自动获取问题
2017-02-23 14:45
716 查看
网络连接失败的处理
看过最前面那篇文章的应该很清楚retryWhen()是什么了。
我再来总结一下,retryWhen()的直面意思就是:发生错误了,接下来该做什么。
retryWhen()是RxJava的一种错误处理机制,当遇到错误时,将错误传递给另一个Observable来决定是否要重新给订阅这个Observable
延迟重试
来想象一个场景:用户用的2G网络或者WiFi信号不稳定,导致网络经常连接失败,其实这个时候只要多努力一下就可以连接成功了,如果此时弹出错误提示,体验肯定不好,所以这里就要用到重试机制
判断错误类型
再模拟一个场景:用户不是手机信号不好,而是根本就没打开网络,此时还傻傻的重试只是浪费电量而已,所以我们可以加一个判断,打开了网络才重试,没有打开网络就继续发送失败的消息。
加入重试超时
继续想,重试也不可能永远进行,一般都会设置一个重试超时的机制。
想一下,没有哪个APP只有一个接口地址吧 - -#,如果你用的Retrofit那么每一个接口返回的Observable都要手动加上上面的重试代码,如果是我,我肯定报警了……所以我们必须把刚刚写的重试代码封装成一个类:
调用代码:
日志:
token失效解决方案
用户长时间不访问app可能导致token的失效,这时如果继续访问app可能会导致用户重新登录降低体验,所以token失效自动重新获取可以增强用户体验。
上代码:
上运行截图:
有木有发现新获取的Token本来应该是2000再重订阅的时候显示却是1000!其实,之所以造成这个的原因是因为对Obserable的工作机制没有理解透彻导致的。在创建一个Obserable的时候,参数的内容,固定的步骤就已经决定好了。 即使在过程中参数发生了改变,retry的时候,还是使用原来的值去请求的。可以使用HttpLoggingInterceptor验证这个。
那总是原始值怎么办?看下面
我们需要每次重试的时候,产生一个新的Obserable。这时候我们可以借助defer操作符来实现,每次调用都是一个新的Obserable。
把请求接口的Obserable用Obserable.defer包裹一下即可。
上截图:
至于次数限制就自己加仿造本片第一段代码。
看过最前面那篇文章的应该很清楚retryWhen()是什么了。
我再来总结一下,retryWhen()的直面意思就是:发生错误了,接下来该做什么。
retryWhen()是RxJava的一种错误处理机制,当遇到错误时,将错误传递给另一个Observable来决定是否要重新给订阅这个Observable
延迟重试
来想象一个场景:用户用的2G网络或者WiFi信号不稳定,导致网络经常连接失败,其实这个时候只要多努力一下就可以连接成功了,如果此时弹出错误提示,体验肯定不好,所以这里就要用到重试机制
判断错误类型
再模拟一个场景:用户不是手机信号不好,而是根本就没打开网络,此时还傻傻的重试只是浪费电量而已,所以我们可以加一个判断,打开了网络才重试,没有打开网络就继续发送失败的消息。
加入重试超时
继续想,重试也不可能永远进行,一般都会设置一个重试超时的机制。
想一下,没有哪个APP只有一个接口地址吧 - -#,如果你用的Retrofit那么每一个接口返回的Observable都要手动加上上面的重试代码,如果是我,我肯定报警了……所以我们必须把刚刚写的重试代码封装成一个类:
public class TryWhenTransaction implements Func1<Observable<? extends 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<Throwable, Observable<?>>() { @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<Integer, Observable<FakeThing>>() { @Override public Observable<FakeThing> 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<FakeThing>() { @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) { } }); }
日志:
token失效解决方案
用户长时间不访问app可能导致token的失效,这时如果继续访问app可能会导致用户重新登录降低体验,所以token失效自动重新获取可以增强用户体验。
上代码:
public void testMethodB() { // 使用token进行网络请求 Observable.just(token).flatMap(new Func1<Integer, Observable<FakeThing>>() { @Override public Observable<FakeThing> 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<? extends Throwable>, Observable<?>>() { @Override public Observable<?> call(final Observable<? extends Throwable> observable) { return observable.flatMap(new Func1<Throwable, Observable<?>>() { @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<FakeThing>() { @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) { } }); }
上运行截图:
有木有发现新获取的Token本来应该是2000再重订阅的时候显示却是1000!其实,之所以造成这个的原因是因为对Obserable的工作机制没有理解透彻导致的。在创建一个Obserable的时候,参数的内容,固定的步骤就已经决定好了。 即使在过程中参数发生了改变,retry的时候,还是使用原来的值去请求的。可以使用HttpLoggingInterceptor验证这个。
那总是原始值怎么办?看下面
我们需要每次重试的时候,产生一个新的Obserable。这时候我们可以借助defer操作符来实现,每次调用都是一个新的Obserable。
把请求接口的Obserable用Obserable.defer包裹一下即可。
public void testMethodC() { //使用defer重新包裹请求 可以在retry后使用新(重新获取的)token进行请求 // 如果不包裹则会使用第一次传进去的token进行请求 Observable.defer(new Func0<Observable<Integer>>() { @Override public Observable<Integer> call() { //这里写请求 由于token过期或者为null传回错误码-1 return Observable.just(token); } }).flatMap(new Func1<Integer, Observable<FakeThing>>() { @Override public Observable<FakeThing> 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<? extends Throwable>, Observable<?>>() { @Override public Observable<?> call(final Observable<? extends Throwable> observable) { return observable.flatMap(new Func1<Throwable, Observable<?>>() { @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<FakeThing>() { @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) { } }); }
上截图:
至于次数限制就自己加仿造本片第一段代码。
相关文章推荐
- LInux下可以使用命令自动与网络的NTP服务器同步时间 CSDN验证码验证失败问题解决
- Angular解决输入框由禁用状态转可用状态自动获取焦点失效问题
- 网络连接不能获取IP问题解决
- 遇到问题---java获取网络文件大小失败getContentLength()为-1 完整解决方法
- 关于ubuntu“下载软件仓库信息失败 检查您的网络连接“问题的解决办法
- 本地网络连接不能自动获取IP的解决办法
- mybatis 数据连接池(解决连接8小时自动失效问题)
- 解决因手机客户端android网络不稳定而导致asmack不能自动重连接openfire的问题
- Unity(C#.net)网络通信问题解决(服务器开启失败,Socket下的“由于目标机器积极拒绝,无法连接”异常)
- 解决Win10无法自动连接隐藏网络的问题
- 遇到问题---java获取网络文件大小失败getContentLength()为-1 完整解决方法
- CentOS 6.9使用Setup配置网络(解决dhcp模式插入网线不自动获取IP的问题)
- Android L 自动获取时区失败问题的解决
- LInux下可以使用命令自动与网络的NTP服务器同步时间 CSDN验证码验证失败问题解决
- Centos在Vmware中,做Net网络后的联网问题(自动获取可以联网,配置静态IP不可以连接)
- office 2010 自动连接网络打印机的问题(保存或者打开极慢) 解决方法
- 理解oracle的网络结构 解决你的连接问题
- 如何解决网络连接问题
- Foxmail接收邮件时,报出"网络操作失败","POP3:****"问题的解决办法
- 网络上连接Windows主机的问题及解决办法