Android RxJava應(yīng)用:網(wǎng)絡(luò)請求出錯重連(結(jié)合Retrofit)

前言

Rxjava,由于其基于事件流的鏈式調(diào)用、邏輯簡潔 & 使用簡單的特點,深受各大 Android開發(fā)者的歡迎。

如果還不了解RxJava,請看文章:Android:這是一篇 清晰 & 易懂的Rxjava 入門教程

  • RxJava如此受歡迎的原因,在于其提供了豐富 & 功能強大的操作符,幾乎能完成所有的功能需求
  • 今天,我將為大家?guī)?Rxjava創(chuàng)建操作符的實際開發(fā)需求場景:網(wǎng)絡(luò)請求出錯重連需求 ,并結(jié)合RetrofitRxJava 實現(xiàn),希望大家會喜歡。

Carson帶你學(xué)RxJava系列文章,包括 原理、操作符、應(yīng)用場景、背壓等等,請關(guān)注看文章:Android:這是一份全面 & 詳細的RxJava學(xué)習(xí)指南


目錄

示意圖

1. 需求場景

示意圖

2. 功能說明

  • 功能需求說明
示意圖

注:關(guān)于 Rxjava中的retryWhen() 操作符的使用請看文章Android RxJava:功能性操作符 全面講解

  • 功能邏輯
示意圖
  • 實例說明
    在本例子中:采用Get方法對 金山詞霸API 發(fā)送網(wǎng)絡(luò)請求
    1. 通過 斷開網(wǎng)絡(luò)連接 模擬 網(wǎng)絡(luò)異常錯誤(恢復(fù)網(wǎng)絡(luò)即可成功發(fā)送請求)
    2. 限制重試次數(shù) = 10次
    3. 采用 Gson 進行數(shù)據(jù)解析
金山詞典

3. 具體實現(xiàn)

下面,我將結(jié)合 RetrofitRxJava 實現(xiàn) 網(wǎng)絡(luò)請求出錯重連 功能

3.1 步驟說明

  1. 添加依賴
  2. 創(chuàng)建 接收服務(wù)器返回數(shù)據(jù) 的類
  3. 創(chuàng)建 用于描述網(wǎng)絡(luò)請求 的接口(區(qū)別于Retrofit傳統(tǒng)形式)
  4. 創(chuàng)建 Retrofit 實例
  5. 創(chuàng)建 網(wǎng)絡(luò)請求接口實例 并 配置網(wǎng)絡(luò)請求參數(shù)(區(qū)別于Retrofit傳統(tǒng)形式)
  6. 發(fā)送網(wǎng)絡(luò)請求(區(qū)別于Retrofit傳統(tǒng)形式)
  7. 發(fā)送網(wǎng)絡(luò)請求
  8. 對返回的數(shù)據(jù)進行處理

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

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

3.2 步驟實現(xiàn)

步驟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. 添加 網(wǎng)絡(luò)權(quán)限
AndroidManifest.xml

<uses-permission android:name="android.permission.INTERNET"/>
步驟2:創(chuàng)建 接收服務(wù)器返回數(shù)據(jù) 的類
  • 金山詞霸API 的數(shù)據(jù)格式說明如下:
// URL模板
http://fy.iciba.com/ajax.php

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

// 參數(shù)說明:
// a:固定值 fy
// f:原文內(nèi)容類型,日語取 ja,中文取 zh,英語取 en,韓語取 ko,德語取 de,西班牙語取 es,法語取 fr,自動則取 auto
// t:譯文內(nèi)容類型,日語取 ja,中文取 zh,英語取 en,韓語取 ko,德語取 de,西班牙語取 es,法語取 fr,自動則取 auto
// w:查詢內(nèi)容
  • 示例
API格式說明
  • 根據(jù) 金山詞霸API 的數(shù)據(jù)格式,創(chuàng)建 接收服務(wù)器返回數(shù)據(jù) 的類:

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;
    }

    //定義 輸出返回數(shù)據(jù) 的方法
    public void show() {
        Log.d("RxJava", content.out );
    }
}
步驟3:創(chuàng)建 用于描述網(wǎng)絡(luò)請求 的接口

采用 注解 + Observable<...>接口描述 網(wǎng)絡(luò)請求參數(shù)

GetRequest_Interface.java

public interface GetRequest_Interface {

    @GET("ajax.php?a=fy&f=auto&t=auto&w=hi%20world")
    Observable<Translation> getCall();
     // 注解里傳入 網(wǎng)絡(luò)請求 的部分URL地址
    // Retrofit把網(wǎng)絡(luò)請求的URL分成了兩部分:一部分放在Retrofit對象里,另一部分放在網(wǎng)絡(luò)請求接口里
    // 如果接口里的url是一個完整的網(wǎng)址,那么放在Retrofit對象里的URL可以忽略
    // 采用Observable<...>接口 
    // getCall()是接受網(wǎng)絡(luò)請求數(shù)據(jù)的方法
}
接下來的步驟均在RxJavafixRxjava.java內(nèi)實現(xiàn)(請看注釋)

RxJavafixRxjava.java

public class RxJavafixRetrofit2 extends AppCompatActivity {

    private static final String TAG = "RxJava";

    // 設(shè)置變量
    // 可重試次數(shù)
    private int maxConnectCount = 10;
    // 當前已重試次數(shù)
    private int currentRetryCount = 0;
    // 重試等待時間
    private int waitRetryTime = 0;

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

        // 步驟1:創(chuàng)建Retrofit對象
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("http://fy.iciba.com/") // 設(shè)置 網(wǎng)絡(luò)請求 Url
                .addConverterFactory(GsonConverterFactory.create()) //設(shè)置使用Gson解析(記得加入依賴)
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) // 支持RxJava
                .build();

        // 步驟2:創(chuàng)建 網(wǎng)絡(luò)請求接口 的實例
        GetRequest_Interface request = retrofit.create(GetRequest_Interface.class);

        // 步驟3:采用Observable<...>形式 對 網(wǎng)絡(luò)請求 進行封裝
        Observable<Translation> observable = request.getCall();

        // 步驟4:發(fā)送網(wǎng)絡(luò)請求 & 通過retryWhen()進行重試
        // 注:主要異常才會回調(diào)retryWhen()進行重試
        observable.retryWhen(new Function<Observable<Throwable>, ObservableSource<?>>() {
            @Override
            public ObservableSource<?> apply(@NonNull Observable<Throwable> throwableObservable) throws Exception {
                // 參數(shù)Observable<Throwable>中的泛型 = 上游操作符拋出的異常,可通過該條件來判斷異常的類型
                return throwableObservable.flatMap(new Function<Throwable, ObservableSource<?>>() {
                    @Override
                    public ObservableSource<?> apply(@NonNull Throwable throwable) throws Exception {

                        // 輸出異常信息
                        Log.d(TAG,  "發(fā)生異常 = "+ throwable.toString());

                        /**
                         * 需求1:根據(jù)異常類型選擇是否重試
                         * 即,當發(fā)生的異常 = 網(wǎng)絡(luò)異常 = IO異常 才選擇重試
                         */
                        if (throwable instanceof IOException){

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

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

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

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


                            }else{
                                // 若重試次數(shù)已 > 設(shè)置重試次數(shù),則不重試
                                // 通過發(fā)送error來停止重試(可在觀察者的onError()中獲取信息)
                                return Observable.error(new Throwable("重試次數(shù)已超過設(shè)置次數(shù) = " +currentRetryCount  + ",即 不再重試"));

                            }
                        }

                        // 若發(fā)生的異常不屬于I/O異常,則不重試
                        // 通過返回的Observable發(fā)送的事件 = Error事件 實現(xiàn)(可在觀察者的onError()中獲取信息)
                        else{
                            return Observable.error(new Throwable("發(fā)生了非網(wǎng)絡(luò)異常(非I/O異常)"));
                        }
                    }
                });
            }
        }).subscribeOn(Schedulers.io())               // 切換到IO線程進行網(wǎng)絡(luò)請求
                .observeOn(AndroidSchedulers.mainThread())  // 切換回到主線程 處理請求結(jié)果
                .subscribe(new Observer<Translation>() {
                    @Override
                    public void onSubscribe(Disposable d) {
                    }

                    @Override
                    public void onNext(Translation result) {
                        // 接收服務(wù)器返回的數(shù)據(jù)
                        Log.d(TAG,  "發(fā)送成功");
                        result.show();
                    }

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

                    @Override
                    public void onComplete() {

                    }
                });

    }
}

3.3 測試結(jié)果

  1. 一開始先通過 斷開網(wǎng)絡(luò)連接 模擬 網(wǎng)絡(luò)異常錯誤,即開始重試;
  2. 等到第3次重試后恢復(fù)網(wǎng)絡(luò)連接,即無發(fā)生網(wǎng)絡(luò)異常錯誤,此時重試成功
示意圖

4. Demo地址

Carson_Ho的Github地址 = RxJava2實戰(zhàn)系列:出錯重連


5. 總結(jié)

  • 本文主要講解了 Rxjava創(chuàng)建操作符的實際開發(fā)需求場景:網(wǎng)絡(luò)請求出錯重連需求 ,并結(jié)合RetrofitRxJava 實現(xiàn)
  • Carson帶你學(xué)RxJava系列文章:

入門
Carson帶你學(xué)Android:這是一篇清晰易懂的Rxjava入門教程
Carson帶你學(xué)Android:面向初學(xué)者的RxJava使用指南
Carson帶你學(xué)Android:RxJava2.0到底更新了什么?
原理
Carson帶你學(xué)Android:圖文解析RxJava原理
Carson帶你學(xué)Android:手把手帶你源碼分析RxJava
使用教程:操作符
Carson帶你學(xué)Android:RxJava操作符教程
Carson帶你學(xué)Android:RxJava創(chuàng)建操作符
Carson帶你學(xué)Android:RxJava功能性操作符
Carson帶你學(xué)Android:RxJava過濾操作符
Carson帶你學(xué)Android:RxJava組合/合并操作符
Carson帶你學(xué)Android:RxJava變換操作符
Carson帶你學(xué)Android:RxJava條件/布爾操作符
實戰(zhàn)
Carson帶你學(xué)Android:什么時候應(yīng)該使用Rxjava?(開發(fā)場景匯總)
Carson帶你學(xué)Android:RxJava線程控制(含實例講解)
Carson帶你學(xué)Android:圖文詳解RxJava背壓策略
Carson帶你學(xué)Android:RxJava、Retrofit聯(lián)合使用匯總(含實例教程)
Carson帶你學(xué)Android:優(yōu)雅實現(xiàn)網(wǎng)絡(luò)請求嵌套回調(diào)
Carson帶你學(xué)Android:網(wǎng)絡(luò)請求輪詢(有條件)
Carson帶你學(xué)Android:網(wǎng)絡(luò)請求輪詢(無條件)
Carson帶你學(xué)Android:網(wǎng)絡(luò)請求出錯重連(結(jié)合Retrofit)
Carson帶你學(xué)Android:合并數(shù)據(jù)源
Carson帶你學(xué)Android:聯(lián)想搜索優(yōu)化
Carson帶你學(xué)Android:功能防抖
Carson帶你學(xué)Android:從磁盤/內(nèi)存緩存中獲取緩存數(shù)據(jù)
Carson帶你學(xué)Android:聯(lián)合判斷


歡迎關(guān)注Carson_Ho的簡書

不定期分享關(guān)于安卓開發(fā)的干貨,追求短、平、快,但卻不缺深度


請點贊!因為你的鼓勵是我寫作的最大動力!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容