您的位置:首页 > 理论基础 > 计算机网络

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

2017-02-23 14:45 716 查看
网络连接失败的处理

看过最前面那篇文章的应该很清楚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) {

}
});
}


上截图:



至于次数限制就自己加仿造本片第一段代码。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐