Android開發 -- RxJava 操作符 map 和 flatMap 新手理解、新手通俗、通俗解釋、通俗、簡單講解

全文閱讀預計 10 分鐘

這可能是你看過的最清晰明瞭的講解

寫在前面RxJava 新手可能會對 map、flatMap 等一系列對操作符產生疑問,但有些例子又很難懂,這裏通過最通俗易懂的方法爲你講述它們究竟是什麼東西,至於以後如何用,你要先知道它們的作用瞭解它們的用法,用什麼是你的需求決定的,所以,當你瞭解的多了自然知道用那個最合適,關鍵是瞭解

正文 👇

map

先說 map,一個簡單的例子 👇

Observable.create(new ObservableOnSubscribe<Integer>() {
    @Override
    public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
        emitter.onNext(1);
        emitter.onNext(2);
        emitter.onNext(3);
        emitter.onComplete();
    }
})
.map(new Function<Integer, String>() {
    @Override
    public String apply(Integer integer) throws Exception {
        Log.e(TAG,"開始淨化處理");
        return integer.toString() + "號水源淨化中 => 淨化完畢";
    }
})
.subscribe(new Observer<String>() {
    @Override
    public void onNext(String s) {
        Log.e(TAG,"接收到淨化完畢的水【" + s + "】");
    }

    @Override
    public void onSubscribe(Disposable d) {}
    @Override
    public void onError(Throwable e) {}
    @Override
    public void onComplete() {}
});

// Log日誌:
// 開始淨化處理
// 接收到淨化完畢的水【1號水源淨化中 => 淨化完畢】
// 開始淨化處理
// 接收到淨化完畢的水【2號水源淨化中 => 淨化完畢】
// 開始淨化處理
// 接收到淨化完畢的水【3號水源淨化中 => 淨化完畢】
  • 非通俗定義:map 操作符對原始 Observable 發射對每一項數據應用一個你選擇對函數,然後返回一個發射這些結果對 Observable

如上,清晰明瞭,將 1、2、3 水源進行淨化處理,最終接收到處理完畢到水,map 相當於一箇中介,onNext(1) 發送的可以是你以後項目中服務器端獲取到的數據,通過 map 這個中介處理,處理後的數據發送到最終接收的地方(Observer 的 onNext 方法中)

當然可能就是以後項目中【先接收服務器端的數據(比如接收到 int 數據)】=>【經過 map 這個中介處理成你想要的數據(轉成 String)】=>【更新到 UI 上(設置到 textView 上)】

flatMap

這裏說 flatMap 👇

  • 非通俗定義:將一個發送事件的 Observable(被觀察者) 變換爲發送多個事件的 Observables,然後將他們發射的事件合併後放進一個單獨的 Observable 裏,需要注意的是 flatMap 並不保證事件順序,也就是說轉換之後的 Observables 的順序不必與轉換之前的序列的順序一致
// 先是兩個很簡單的類
class ClassRoom {
    String classRoomName; // 教室的名字
    List<Student> list = new ArrayList<>(); // 教室中的學生列表
}
class Student {
    Student(String n) {
        this.name = n;
    }
    String name;// 學生的名字
}

// 添加簡單的測試數據
List<ClassRoom> school = new ArrayList<>();
// 1班
ClassRoom c1 = new ClassRoom();
c1.classRoomName = "1班";
c1.list.add(new Student("學生A"));
c1.list.add(new Student("學生B"));
c1.list.add(new Student("學生C"));
c1.list.add(new Student("學生D"));

// 2班
ClassRoom c2 = new ClassRoom();
c2.classRoomName = "2班";
c2.list.add(new Student("學生E"));
c2.list.add(new Student("學生F"));
c2.list.add(new Student("學生G"));
c2.list.add(new Student("學生H"));

// 3班
ClassRoom c3 = new ClassRoom();
c3.classRoomName = "3班";
c3.list.add(new Student("學生I"));
c3.list.add(new Student("學生J"));
c3.list.add(new Student("學生K"));
c3.list.add(new Student("學生L"));

// 添加
school.add(c1);
school.add(c2);
school.add(c3);

// 關鍵部分
Observable.fromIterable(school)
    .flatMap(new Function<ClassRoom, ObservableSource<Student>>() {
        @Override
        public ObservableSource<Student> apply(ClassRoom classRoom) throws Exception {
            Log.e(TAG, "ClassRoom name:" + classRoom.classRoomName);
            return Observable.fromIterable(classRoom.list).delay(10, TimeUnit.MILLISECONDS);
        }
    })
    .subscribe(new Observer<Student>() {
        @Override
        public void onNext(Student student) {
            Log.e(TAG, "學生姓名:" + student.name);
        }

        @Override
        public void onSubscribe(Disposable d) {}
        @Override
        public void onError(Throwable e) {}
        @Override
        public void onComplete() {}
    });

首先 fromIterable 是什麼:相當於 👇

onNext(school.get(0));
onNext(school.get(1));
onNext(school.get(2));
onComplete();

那麼 Observable.fromIterable(classRoom.list).delay(10, TimeUnit.MILLISECONDS); 是什麼?相當於原有的基礎上在發送之前加了 10 毫秒的延遲,TimeUnit 表示事件單位,這裏用的是 MILLISECONDS 也就是毫秒,秒則是 SECONDS,這裏加一個延遲主要是爲了模擬網絡延遲的效果

看一下日誌

// 加了 10 毫秒延遲的
ClassRoom name:1班
ClassRoom name:2班
ClassRoom name:3班
學生姓名:學生A
學生姓名:學生C
學生姓名:學生B
學生姓名:學生E
學生姓名:學生D
學生姓名:學生G
學生姓名:學生F
學生姓名:學生H
學生姓名:學生J
學生姓名:學生K
學生姓名:學生I
學生姓名:學生L

// 不加延遲的
ClassRoom name:1班
學生姓名:學生A
學生姓名:學生B
學生姓名:學生C
學生姓名:學生D
ClassRoom name:2班
學生姓名:學生E
學生姓名:學生F
學生姓名:學生G
學生姓名:學生H
ClassRoom name:3班
學生姓名:學生I
學生姓名:學生J
學生姓名:學生K
學生姓名:學生L

可以看到,就像上面 非通俗定義 中所說的並不保證事件的順序(A、C、B、E、D、G...),加延遲主要是爲了更好的顯現出它不保證順序的特性,並不是因爲有了延遲纔不保證順序,而是無論如何 flatMap 都不保證你接收到事件的順序

我們這個例子相當於 一個學校 中有 3 個班級,我們想要打印出所有 班級的名字 以及 每個班級每個學生的名字,但是你可能會看到覺得代碼反而變多了

  • 首先這只是個例子
  • RxJava 並不是說讓代碼變少,而是讓代碼邏輯更清晰,試想清晰的代碼邏輯和少又難懂又嵌套了各種循環的代碼那個更好呢
  • 當你瞭解的操作符多了,在項目中遇到實際問題你自然知道哪個最適合你

通過這個例子可以看出,flatMap 是將發送事件的 Observable 經過 flatMap 變換爲多個發送事件的 Observables 然後再將他們發射後的事件合併後放進一個單獨的 Observable 裏,看下圖 👇


再來看更加詳細的分解 👇


由上圖再根據我們的例子得知:c1、c2、c3 經過 flatMap 後,依次經歷了 Observable1、2、3(圖中橢圓形的內容),打印了一下班級名,然後又分別把他們轉換爲了 StudentA - StudentL,其中 StudentA - StudentDc1 得到,以此類推。

想必此時已經清晰明瞭了,當然現在你已經知道 flatMap 並不保證事件的順序,如果想要保證順序的話需要使用 concatMap

使用場景

map 適用於一對一轉換

flatMap 適用於一對多,多對多

碼字不易,還請動動小手左側欄中點贊 👍,3Q

發佈了15 篇原創文章 · 獲贊 7 · 訪問量 3002
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章