RxJava2 實戰知識梳理(6) 基於錯誤類型的重試請求

1、前言

1.1 應用背景

在網絡請求時,有時候會出現須要進行重試的狀況,重試的時候,有如下幾點須要注意:java

  • 限制重試的次數
  • 根據錯誤類型,判斷是否要重試
  • 根據錯誤類型,等待特定的時間以後再去重試

咱們先來看一下目前的一些網絡框架是怎麼作的?經過分析Volley的源碼,能夠從BasicNetwork的代碼中看到,它是將網絡請求的代碼都放在一個無限的while(true)循環當中,若是發生了異常,會在其中的catch語句中進行處理,若是須要繼續重試,那麼就吞掉這個異常,並將重試次數加1,這樣就會進入下一次的while(true)循環去訪問網絡;若是不須要重試,那麼就拋出這個異常,退出這個無限循環。也就是實現了前面兩點需求。網絡

下面咱們就來演示如何經過RxJava2來輕鬆實現上面的三點需求,經過這篇文章,咱們將學習retryWhen操做符的具體用法,retryWhenrepeatWhen常常被你們用來比較,若是對repeatWhen感興趣的同窗能夠閱讀上一篇文章 RxJava2 實戰知識梳理(5) - 簡單及進階的輪詢操做app

2.2 示例代碼

在下面的例子中,咱們一共發起了五次請求,也就是subscribe中的代碼,其中前四次請求都調用onError方法通知下游請求失敗,同時帶上了自定義的錯誤信息wait_shortwait_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

2、示例解析

2.1 retryWhen 介紹

retryWhen的原理圖以下所示: 函數

retryWhen 原理圖
retryWhen提供了 重訂閱 的功能,對於 retryWhen來講,它的重訂閱觸發有兩點要素:

  • 上游通知retryWhen本次訂閱流已經完成,詢問其是否須要重訂閱,該詢問是以onError事件觸發的。
  • retryWhen根據onError的類型,決定是否須要重訂閱,它經過返回一個ObservableSource<?>來通知,若是該ObservableSource返回onComplete/onError,那麼不會觸發重訂閱;若是發送onNext,那麼會觸發重訂閱。

實現retryWhen的關鍵在於如何定義它的Function參數:學習

  • Function的輸入是一個Observable<Throwable>,輸出是一個泛型ObservableSource<?>。若是咱們接收Observable<Throwable>發送的消息,那麼就能夠獲得上游發送的錯誤類型,並根據該類型進行響應的處理。
  • 若是輸出的Observable發送了onComplete或者onError則表示不須要重訂閱,結束整個流程;不然觸發重訂閱的操做。也就是說,它 僅僅是做爲一個是否要觸發重訂閱的通知onNext發送的是什麼數據並不重要。
  • 對於每一次訂閱的數據流 Function 函數只會回調一次,而且是在onError(Throwable throwable)的時候觸發,它不會收到任何的onNext事件。
  • Function函數中,必須對輸入的 Observable進行處理,這裏咱們使用的是flatMap操做符接收上游的數據,對於flatMap的解釋,你們能夠參考 RxJava2 實戰知識梳理(4) - 結合 Retrofit 請求新聞資訊

    2.2 retryWhen 和 repeatWhen 對比

    RxJava2 實戰知識梳理(5) - 簡單及進階的輪詢操做 中咱們已經對repeatWhen進行了介紹,讓咱們再來看一下它的原理圖: spa

    repeatWhen 原理圖
    能夠看到,retryWhenrepeatWhen最大的不一樣就是:retryWhen是收到onError後觸發是否要重訂閱的詢問,而repeatWhen是經過onComplete觸發。

    2.3 根據 Throwable 的類型選擇響應的重試策略

    因爲上游能夠經過onError(Throwable throwable)中的異常通知retryWhen,那麼咱們就能夠根據異常的類型來決定重試的策略。3d

    就像咱們在上面例子中作的那樣,咱們經過flatMap操做符獲取到異常的類型,而後根據異常的類型選擇動態地決定延遲重試的時間,再用Timer操做符實現延遲重試;固然,對於一些異常,咱們能夠直接選擇不重試,即直接返回Observable.empty或者Observable.error(Throwable throwable)


    更多文章,歡迎訪問個人 Android 知識梳理系列:

相關文章
相關標籤/搜索