在网络请求时,有时候会出现须要进行重试的状况,重试的时候,有如下几点须要注意:java
咱们先来看一下目前的一些网络框架是怎么作的?经过分析Volley
的源码,能够从BasicNetwork
的代码中看到,它是将网络请求的代码都放在一个无限的while(true)
循环当中,若是发生了异常,会在其中的catch
语句中进行处理,若是须要继续重试,那么就吞掉这个异常,并将重试次数加1
,这样就会进入下一次的while(true)
循环去访问网络;若是不须要重试,那么就抛出这个异常,退出这个无限循环。也就是实现了前面两点需求。网络
下面咱们就来演示如何经过RxJava2
来轻松实现上面的三点需求,经过这篇文章,咱们将学习retryWhen
操做符的具体用法,retryWhen
和repeatWhen
常常被你们用来比较,若是对repeatWhen
感兴趣的同窗能够阅读上一篇文章 RxJava2 实战知识梳理(5) - 简单及进阶的轮询操做。app
在下面的例子中,咱们一共发起了五次请求,也就是subscribe
中的代码,其中前四次请求都调用onError
方法通知下游请求失败,同时带上了自定义的错误信息wait_short
和wait_long
,第五次才返回正确的数据。框架
当咱们收到错误以后,会根据错误的类型肯定重试的时间,同时,咱们还保存了当前重试的次数,避免无限次的重试请求。若是须要重试,那么经过Timer
操做符延时指定的时间,不然返回Observable.error(Throwable)
放弃重试。dom
public class RetryActivity extends AppCompatActivity {
private static final String TAG = RetryActivity.class.getSimpleName();
private static final String MSG_WAIT_SHORT = "wait_short";
private static final String MSG_WAIT_LONG = "wait_long";
private static final String[] MSG_ARRAY = new String[] {
MSG_WAIT_SHORT,
MSG_WAIT_SHORT,
MSG_WAIT_LONG,
MSG_WAIT_LONG
};
private TextView mTvRetryWhen;
private CompositeDisposable mCompositeDisposable;
private int mMsgIndex;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_retry);
mTvRetryWhen = (TextView) findViewById(R.id.tv_retry_when);
mTvRetryWhen.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startRetryRequest();
}
});
mCompositeDisposable = new CompositeDisposable();
}
private void startRetryRequest() {
Observable<String> observable = Observable.create(new ObservableOnSubscribe<String>() {
@Override
public void subscribe(ObservableEmitter<String> e) throws Exception {
int msgLen = MSG_ARRAY.length;
doWork();
//模拟请求的结果,前四次都返回失败,并将失败信息递交给retryWhen。
if (mMsgIndex < msgLen) { //模拟请求失败的状况。
e.onError(new Throwable(MSG_ARRAY[mMsgIndex]));
mMsgIndex++;
} else { //模拟请求成功的状况。
e.onNext("Work Success");
e.onComplete();
}
}
}).retryWhen(new Function<Observable<Throwable>, ObservableSource<?>>() {
private int mRetryCount;
@Override
public ObservableSource<?> apply(Observable<Throwable> throwableObservable) throws Exception {
return throwableObservable.flatMap(new Function<Throwable, ObservableSource<?>>() {
@Override
public ObservableSource<?> apply(Throwable throwable) throws Exception {
String errorMsg = throwable.getMessage();
long waitTime = 0;
switch (errorMsg) {
case MSG_WAIT_SHORT:
waitTime = 2000;
break;
case MSG_WAIT_LONG:
waitTime = 4000;
break;
default:
break;
}
Log.d(TAG, "发生错误,尝试等待时间=" + waitTime + ",当前重试次数=" + mRetryCount);
mRetryCount++;
return waitTime > 0 && mRetryCount <= 4 ? Observable.timer(waitTime, TimeUnit.MILLISECONDS) : Observable.error(throwable);
}
});
}
});
DisposableObserver<String> disposableObserver = new DisposableObserver<String>() {
@Override
public void onNext(String value) {
Log.d(TAG, "DisposableObserver onNext=" + value);
}
@Override
public void onError(Throwable e) {
Log.d(TAG, "DisposableObserver onError=" + e);
}
@Override
public void onComplete() {
Log.d(TAG, "DisposableObserver onComplete");
}
};
observable.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(disposableObserver);
mCompositeDisposable.add(disposableObserver);
}
private void doWork() {
long workTime = (long) (Math.random() * 500) + 500;
try {
Log.d(TAG, "doWork start, threadId=" + Thread.currentThread().getId());
Thread.sleep(workTime);
Log.d(TAG, "doWork finished");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
复制代码
上述代码的运行结果为,红框中的间隔就是每次等待重试的时间: ide
retryWhen
的原理图以下所示: 函数
retryWhen
提供了
重订阅 的功能,对于
retryWhen
来讲,它的重订阅触发有两点要素:
retryWhen
本次订阅流已经完成,询问其是否须要重订阅,该询问是以onError
事件触发的。retryWhen
根据onError
的类型,决定是否须要重订阅,它经过返回一个ObservableSource<?>
来通知,若是该ObservableSource
返回onComplete/onError
,那么不会触发重订阅;若是发送onNext
,那么会触发重订阅。实现retryWhen
的关键在于如何定义它的Function
参数:学习
Function
的输入是一个Observable<Throwable>
,输出是一个泛型ObservableSource<?>
。若是咱们接收Observable<Throwable>
发送的消息,那么就能够获得上游发送的错误类型,并根据该类型进行响应的处理。Observable
发送了onComplete
或者onError
则表示不须要重订阅,结束整个流程;不然触发重订阅的操做。也就是说,它 仅仅是做为一个是否要触发重订阅的通知,onNext
发送的是什么数据并不重要。onError(Throwable throwable)
的时候触发,它不会收到任何的onNext
事件。Function
函数中,必须对输入的 Observable进行处理,这里咱们使用的是flatMap
操做符接收上游的数据,对于flatMap
的解释,你们能够参考 RxJava2 实战知识梳理(4) - 结合 Retrofit 请求新闻资讯 。 在 RxJava2 实战知识梳理(5) - 简单及进阶的轮询操做 中咱们已经对repeatWhen
进行了介绍,让咱们再来看一下它的原理图: spa
retryWhen
和repeatWhen
最大的不一样就是:retryWhen
是收到onError
后触发是否要重订阅的询问,而repeatWhen
是经过onComplete
触发。 因为上游能够经过onError(Throwable throwable)
中的异常通知retryWhen
,那么咱们就能够根据异常的类型来决定重试的策略。3d
就像咱们在上面例子中作的那样,咱们经过flatMap
操做符获取到异常的类型,而后根据异常的类型选择动态地决定延迟重试的时间,再用Timer
操做符实现延迟重试;固然,对于一些异常,咱们能够直接选择不重试,即直接返回Observable.empty
或者Observable.error(Throwable throwable)
。