在網絡請求時,有時候會出現須要進行重試的狀況,重試的時候,有如下幾點須要注意: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)
。