Android RxJava應用:網絡請求出錯重連(結合Retrofit)

前言

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

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

  • RxJava如此受歡迎的原因,在于其提供了豐富 & 功能強大的操作符,幾乎能完成所有的功能需求
  • 今天,我將為大家帶來 Rxjava創建操作符的實際開發需求場景:網絡請求出錯重連需求 ,并結合RetrofitRxJava 實現,希望大家會喜歡。

Carson帶你學RxJava系列文章,包括 原理、操作符、應用場景、背壓等等,請關注看文章:Android:這是一份全面 & 詳細的RxJava學習指南


目錄

示意圖

1. 需求場景

示意圖

2. 功能說明

  • 功能需求說明
示意圖

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

  • 功能邏輯
示意圖
  • 實例說明
    在本例子中:采用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格式說明
  • 根據 金山詞霸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. 等到第3次重試后恢復網絡連接,即無發生網絡異常錯誤,此時重試成功
示意圖

4. Demo地址

Carson_Ho的Github地址 = RxJava2實戰系列:出錯重連


5. 總結

  • 本文主要講解了 Rxjava創建操作符的實際開發需求場景:網絡請求出錯重連需求 ,并結合RetrofitRxJava 實現
  • Carson帶你學RxJava系列文章:

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


歡迎關注Carson_Ho的簡書

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


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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,606評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,582評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,540評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,028評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,801評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,223評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,294評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,442評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,976評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,800評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,996評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,543評論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,233評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,662評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,926評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,702評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,991評論 2 374

推薦閱讀更多精彩內容