1. 程式人生 > >Android RxJava 實戰系列:優雅實現 網路請求巢狀回撥

Android RxJava 實戰系列:優雅實現 網路請求巢狀回撥

轉自-----http://blog.csdn.net/carson_ho/article/details/78315696,請為大神打call



前言

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

Github截圖

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


  • RxJava如此受歡迎的原因,在於其提供了豐富 & 功能強大的操作符,幾乎能完成所有的功能需求
  • 今天,我將為大家帶來 Rxjava
    建立操作符的實際開發需求場景:網路請求巢狀回撥 需求 ,並結合RetrofitRxJava 實現,希望大家會喜歡。 

  1. 本系列文章主要基於 Rxjava 2.0 
  2. 接下來的時間,我將持續推出 Android中 Rxjava 2.0 的一系列文章,包括原理、操作符、應用場景、背壓等等 ,有興趣可以繼續關注Carson_Ho的安卓開發筆記!!
    示意圖

目錄

示意圖


1. 需求場景

1.1 背景

需要進行巢狀網路請求:即在第1個網路請求成功後,繼續再進行一次網路請求

如 先進行 使用者註冊 的網路請求, 待註冊成功後回再繼續傳送 使用者登入 的網路請求

1.2 衝突

巢狀實現網路請求較為複雜,即巢狀呼叫函式

下面展示的是結合 Retrofit 與 RxJava的基本用法,即未用操作符前

// 傳送註冊網路請求的函式方法
    private void register() {
        api.register(new RegisterRequest())
                .subscribeOn(Schedulers.io())               //在IO執行緒進行網路請求
.observeOn(AndroidSchedulers.mainThread()) //回到主執行緒去處理請求結果 .subscribe(new Consumer<RegisterResponse>() { @Override public void accept(RegisterResponse registerResponse) throws Exception { Toast.makeText(MainActivity.this, "註冊成功", Toast.LENGTH_SHORT).show(); login(); //註冊成功, 呼叫登入的方法 } }, new Consumer<Throwable>() { @Override public void accept(Throwable throwable) throws Exception { Toast.makeText(MainActivity.this, "註冊失敗", Toast.LENGTH_SHORT).show(); } }); } // 傳送登入網路請求的函式方法 private void login() { api.login(new LoginRequest()) .subscribeOn(Schedulers.io()) //在IO執行緒進行網路請求 .observeOn(AndroidSchedulers.mainThread()) //回到主執行緒去處理請求結果 .subscribe(new Consumer<LoginResponse>() { @Override public void accept(LoginResponse loginResponse) throws Exception { Toast.makeText(MainActivity.this, "登入成功", Toast.LENGTH_SHORT).show(); } }, new Consumer<Throwable>() { @Override public void accept(Throwable throwable) throws Exception { Toast.makeText(MainActivity.this, "登入失敗", Toast.LENGTH_SHORT).show(); } }); }

1.3 解決方案

結合 RxJava2中的變換操作符FlatMap()實現巢狀網路請求

關於該操作符的使用具體請看文章:Android RxJava:圖文詳解 變換操作符


2. 功能說明

  • 實現功能:傳送巢狀網路請求(將英文翻譯成中文,翻譯兩次)

    1. 為了讓大家都能完成Demo,所以通過 公共的金山詞霸API 來模擬 “註冊 - 登入”巢狀網路請求
    2. 即先翻譯 Register(註冊),再翻譯 Login(登入)
  • 實現方案:採用Get方法對 金山詞霸API 傳送網路請求

    採用 Gson 進行資料解析

金山詞典


3. 具體實現

下面我將結合 Retrofit 與RxJava 實現網路請求巢狀

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 的資料格式,建立 接收伺服器返回資料 的類: 
    為了演示是2個網路請求,所以對應設定2個接收伺服器的資料類
<-- Translation1.java -->
public class Translation1 {
    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);

    }
}

<-- Translation2.java -->
public class Translation2 {
    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 {

    // 網路請求1
    @GET("ajax.php?a=fy&f=auto&t=auto&w=hi%20register")
    Observable<Translation1> getCall();

    // 網路請求2
    @GET("ajax.php?a=fy&f=auto&t=auto&w=hi%20login")
    Observable<Translation2> getCall_2();

    // 註解裡傳入 網路請求 的部分URL地址
    // Retrofit把網路請求的URL分成了兩部分:一部分放在Retrofit物件裡,另一部分放在網路請求接口裡
    // 如果接口裡的url是一個完整的網址,那麼放在Retrofit物件裡的URL可以忽略
    // 採用Observable<...>介面
    // getCall()是接受網路請求資料的方法

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

MainActivity.java

public class MainActivity extends AppCompatActivity {

        private static final String TAG = "Rxjava";

        // 定義Observable介面型別的網路請求物件
        Observable<Translation1> observable1;
        Observable<Translation2> observable2;

        @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<...>形式 對 2個網路請求 進行封裝
            observable1 = request.getCall();
            observable2 = request.getCall_2();


            observable1.subscribeOn(Schedulers.io())               // (初始被觀察者)切換到IO執行緒進行網路請求1
                       .observeOn(AndroidSchedulers.mainThread())  // (新觀察者)切換到主執行緒 處理網路請求1的結果
                       .doOnNext(new Consumer<Translation1>() {
                        @Override
                        public void accept(Translation1 result) throws Exception {
                            Log.d(TAG, "第1次網路請求成功");
                            result.show();
                            // 對第1次網路請求返回的結果進行操作 = 顯示翻譯結果
                        }
                    })

                    .observeOn(Schedulers.io())                 // (新被觀察者,同時也是新觀察者)切換到IO執行緒去發起登入請求
                                                                // 特別注意:因為flatMap是對初始被觀察者作變換,所以對於舊被觀察者,它是新觀察者,所以通過observeOn切換執行緒
                                                                // 但對於初始觀察者,它則是新的被觀察者
                    .flatMap(new Function<Translation1, ObservableSource<Translation2>>() { // 作變換,即作巢狀網路請求
                        @Override
                        public ObservableSource<Translation2> apply(Translation1 result) throws Exception {
                            // 將網路請求1轉換成網路請求2,即傳送網路請求2
                            return observable2;
                        }
                    })

                    .observeOn(AndroidSchedulers.mainThread())  // (初始觀察者)切換到主執行緒 處理網路請求2的結果
                    .subscribe(new Consumer<Translation2>() {
                        @Override
                        public void accept(Translation2 result) throws Exception {
                            Log.d(TAG, "第2次網路請求成功");
                            result.show();
                            // 對第2次網路請求返回的結果進行操作 = 顯示翻譯結果
                        }
                    }, new Consumer<Throwable>() {
                        @Override
                        public void accept(Throwable throwable) throws Exception {
                            System.out.println("登入失敗");
                        }
                    });
    }
}

3.3 測試結果

示意圖


4. Demo地址

Carson_Ho的Github地址 = RxJava2實戰系列:網路請求巢狀回撥

喜歡的麻煩點個star


5. 總結

  • 本文主要講解了 Rxjava 變換操作符的實際開發需求場景:巢狀回撥需求 ,並結合Retrofit 與RxJava 實現
  • 接下來的時間,我將持續推出 Android中 Rxjava 2.0 的一系列文章,包括原理、操作符、應用場景、背壓等等 ,有興趣可以繼續關注Carson_Ho的安卓開發筆記!!

示意圖