1. 程式人生 > >Android RxJava操作符的學習---功能性操作符--網路請求出錯重連(結合Retrofit)

Android RxJava操作符的學習---功能性操作符--網路請求出錯重連(結合Retrofit)

1. 需求場景

 

2. 功能說明

  • 功能需求說明

 

 

  • 功能邏輯

 例項說明
在本例子中:採用Get方法對 金山詞霸API 傳送網路請求

  1. 通過 斷開網路連線 模擬 網路異常錯誤(恢復網路即可成功傳送請求)
  2. 限制重試次數 = 10次
  3. 採用 Gson 進行資料解析

3. 具體實現

下面,我將結合 RetrofitRxJava 實現 網路請求出錯重連 功能

3.1 步驟說明

  1. 新增依賴
  2. 建立 接收伺服器返回資料 的類
  3. 建立 用於描述網路請求 的介面(區別於Retrofit
    傳統形式)
  4. 建立 Retrofit 例項
  5. 建立 網路請求介面例項 並 配置網路請求引數(區別於Retrofit傳統形式)
  6. 傳送網路請求(區別於Retrofit傳統形式)
  7. 傳送網路請求
  8. 對返回的資料進行處理

下面,將進行詳細的功能說明。

本例項側重於說明 RxJava 的輪詢需求,關於Retrofit的使用請看文章:這是一份很詳細的 Retrofit 2.0 使用教程(含例項講解)

3.2 步驟實現

步驟1: 新增依賴

a. 在 Gradle加入Retrofit庫的依賴

build.gradle

dependencies {

// Android 支援 Rxjava
// 此處一定要注意使用RxJava2的版本
compile 'io.reactivex.rxjava2:rxjava:2.0.1'
compile 'io.reactivex.rxjava2:rxandroid:2.0.1'

// Android 支援 Retrofit
compile 'com.squareup.retrofit2:retrofit:2.1.0'

// 銜接 Retrofit & RxJava
// 此處一定要注意使用RxJava2的版本
compile 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0'

// 支援Gson解析
compile 'com.squareup.retrofit2:converter-gson:2.1.0'

}

b. 新增 網路許可權
AndroidManifest.xml

<uses-permission android:name="android.permission.INTERNET"/>

步驟2:建立 接收伺服器返回資料 的類

  • 金山詞霸API 的資料格式說明如下:
// URL模板
http://fy.iciba.com/ajax.php

// URL例項
http://fy.iciba.com/ajax.php?a=fy&f=auto&t=auto&w=hello%20world

// 引數說明:
// a:固定值 fy
// f:原文內容型別,日語取 ja,中文取 zh,英語取 en,韓語取 ko,德語取 de,西班牙語取 es,法語取 fr,自動則取 auto
// t:譯文內容型別,日語取 ja,中文取 zh,英語取 en,韓語取 ko,德語取 de,西班牙語取 es,法語取 fr,自動則取 auto
// w:查詢內容
  • 根據 金山詞霸API 的資料格式,建立 接收伺服器返回資料 的類:

Translation.java

public class Translation {

    private int status;

    private content content;
    private static class content {
        private String from;
        private String to;
        private String vendor;
        private String out;
        private int errNo;
    }

    //定義 輸出返回資料 的方法
    public void show() {
        Log.d("RxJava", content.out );
    }
}

步驟3:建立 用於描述網路請求 的介面

採用 註解 + Observable<...>介面描述 網路請求引數

GetRequest_Interface.java

public interface GetRequest_Interface {

    @GET("ajax.php?a=fy&f=auto&t=auto&w=hi%20world")
    Observable<Translation> getCall();
     // 註解裡傳入 網路請求 的部分URL地址
    // Retrofit把網路請求的URL分成了兩部分:一部分放在Retrofit物件裡,另一部分放在網路請求接口裡
    // 如果接口裡的url是一個完整的網址,那麼放在Retrofit物件裡的URL可以忽略
    // 採用Observable<...>介面 
    // getCall()是接受網路請求資料的方法
}

接下來的步驟均在RxJavafixRxjava.java內實現(請看註釋)

RxJavafixRxjava.java

public class RxJavafixRetrofit2 extends AppCompatActivity {

    private static final String TAG = "RxJava";

    // 設定變數
    // 可重試次數
    private int maxConnectCount = 10;
    // 當前已重試次數
    private int currentRetryCount = 0;
    // 重試等待時間
    private int waitRetryTime = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 步驟1:建立Retrofit物件
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("http://fy.iciba.com/") // 設定 網路請求 Url
                .addConverterFactory(GsonConverterFactory.create()) //設定使用Gson解析(記得加入依賴)
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) // 支援RxJava
                .build();

        // 步驟2:建立 網路請求介面 的例項
        GetRequest_Interface request = retrofit.create(GetRequest_Interface.class);

        // 步驟3:採用Observable<...>形式 對 網路請求 進行封裝
        Observable<Translation> observable = request.getCall();

        // 步驟4:傳送網路請求 & 通過retryWhen()進行重試
        // 注:主要異常才會回撥retryWhen()進行重試
        observable.retryWhen(new Function<Observable<Throwable>, ObservableSource<?>>() {
            @Override
            public ObservableSource<?> apply(@NonNull Observable<Throwable> throwableObservable) throws Exception {
                // 引數Observable<Throwable>中的泛型 = 上游操作符丟擲的異常,可通過該條件來判斷異常的型別
                return throwableObservable.flatMap(new Function<Throwable, ObservableSource<?>>() {
                    @Override
                    public ObservableSource<?> apply(@NonNull Throwable throwable) throws Exception {

                        // 輸出異常資訊
                        Log.d(TAG,  "發生異常 = "+ throwable.toString());

                        /**
                         * 需求1:根據異常型別選擇是否重試
                         * 即,當發生的異常 = 網路異常 = IO異常 才選擇重試
                         */
                        if (throwable instanceof IOException){

                            Log.d(TAG,  "屬於IO異常,需重試" );

                            /**
                             * 需求2:限制重試次數
                             * 即,當已重試次數 < 設定的重試次數,才選擇重試
                             */
                            if (currentRetryCount < maxConnectCount){

                                // 記錄重試次數
                                currentRetryCount++;
                                Log.d(TAG,  "重試次數 = " + currentRetryCount);

                                /**
                                 * 需求2:實現重試
                                 * 通過返回的Observable傳送的事件 = Next事件,從而使得retryWhen()重訂閱,最終實現重試功能
                                 *
                                 * 需求3:延遲1段時間再重試
                                 * 採用delay操作符 = 延遲一段時間傳送,以實現重試間隔設定
                                 *
                                 * 需求4:遇到的異常越多,時間越長
                                 * 在delay操作符的等待時間內設定 = 每重試1次,增多延遲重試時間1s
                                 */
                                // 設定等待時間
                                waitRetryTime = 1000 + currentRetryCount* 1000;
                                Log.d(TAG,  "等待時間 =" + waitRetryTime);
                                return Observable.just(1).delay(waitRetryTime, TimeUnit.MILLISECONDS);


                            }else{
                                // 若重試次數已 > 設定重試次數,則不重試
                                // 通過傳送error來停止重試(可在觀察者的onError()中獲取資訊)
                                return Observable.error(new Throwable("重試次數已超過設定次數 = " +currentRetryCount  + ",即 不再重試"));

                            }
                        }

                        // 若發生的異常不屬於I/O異常,則不重試
                        // 通過返回的Observable傳送的事件 = Error事件 實現(可在觀察者的onError()中獲取資訊)
                        else{
                            return Observable.error(new Throwable("發生了非網路異常(非I/O異常)"));
                        }
                    }
                });
            }
        }).subscribeOn(Schedulers.io())               // 切換到IO執行緒進行網路請求
                .observeOn(AndroidSchedulers.mainThread())  // 切換回到主執行緒 處理請求結果
                .subscribe(new Observer<Translation>() {
                    @Override
                    public void onSubscribe(Disposable d) {
                    }

                    @Override
                    public void onNext(Translation result) {
                        // 接收伺服器返回的資料
                        Log.d(TAG,  "傳送成功");
                        result.show();
                    }

                    @Override
                    public void onError(Throwable e) {
                        // 獲取停止重試的資訊
                        Log.d(TAG,  e.toString());
                    }

                    @Override
                    public void onComplete() {

                    }
                });

    }
}

3.3 測試結果

  1. 一開始先通過 斷開網路連線 模擬 網路異常錯誤,即開始重試;
  2. 等到第4次重試後恢復網路連線,即無發生網路異常錯誤,此時重試成功