RxJava2原始碼解讀之 Map、FlatMap

三好碼農發表於2018-08-05

RxJava給我們提供了很多變換的操作符,map、flatMap就是比較常用的操作符,一般我們使用的時候,都是看官方文件來了解每個操作符的含義,但是我自己感覺下來,看官方文件使用沒問題,但是總有一點隔靴搔癢的意思,所以我還要去RxJava的原始碼一探究竟,做到心中有數。

我們先從相對簡單的 Map 開始

Map

官方定義:transform the items emitted by an Observable by applying a function to each item

拙劣的翻譯:應用一個函式 轉換所有的被髮射的item

官方的圖解:

map 圖例.png

到這裡我們總結一下:

  • map 轉換是一對一的,原來發射了幾個資料,轉換之後還是幾個
  • map 轉換可以改變發射的資料型別

這裡丟擲一個問題,map 呼叫我們提供的function進行轉換,那麼這個function在什麼時候被呼叫?在哪個執行緒被呼叫?(這個對我們實際工程中使用map有意義,知道程式碼被執行的執行緒是必須的)

廢話不多說,進入原始碼

Map原始碼

Observable類是RxJava的門面,基本上所有的轉換符都在這裡定義,直接看Map 的方法定義

map 方法.png

可以看到,Function類,泛型有2個引數,第一個是原資料型別,第二個是轉換後的資料型別,最終返回的是ObservableMap 類(RxJava的類命名很規範,如果是Observable型別的就是Observable開頭 + 具體的操作符名稱,如果是Observer型別的 就是 具體的操作符名稱 + Observer結尾)我們進入ObservableMap類,Observable類之前的文章有提到過,subscribeActual 是個重要的鉤子方法,所以我們直接看ObservableMap如何重寫該方法的

ObservaleMap.png

方法程式碼就一行,呼叫裝飾的Observable的subscribe方法,傳遞一個MapObserver物件,Observer類我們就比較熟悉了,我們這裡主要看onNext方法

map onnext.png

程式碼也很簡單,紅框標識的就是 mapper 轉換函式被呼叫的地方,得到轉換後的物件v,傳遞給被裝飾的Observer 的onNext方法,到這裡,一次資料的map轉換就結束了。原始碼的實現還是很簡單的,在我們瞭解了原始碼的實現後,思路會更清晰,寫程式碼時也會更有把握。

現在我們來解答前面我們丟擲的問題,Function在什麼時候被呼叫?在哪個執行緒被呼叫? Function呼叫的地方已經清楚了,在ObserverMap 的 onNext方法中,那麼呼叫的執行緒呢,因為是在Observer方法中被呼叫,所以如果在map 之前 呼叫了 ObserverOn 方法設定監聽執行緒,那麼就在該監聽執行緒,如果沒有設定 ObserverOn 但是設定了 SubscribeOn方法設定發射執行緒,那麼就在該 發射執行緒,如果SubscribeOn也沒有設定,那就在Observable的建立執行緒。

到此Map 就介紹完了,接下來是Map 的好兄弟 FlatMap,呼叫邏輯稍微複雜一點點,看官們耐心 -。-

###FlatMap

官方定義:transform the items emitted by an Observable into Observables, then flatten the emissions from those into a single Observable

拙劣的翻譯:應用一個函式 轉換所有的被髮射的item從一個Observable轉成為多個Observable,並將所有要發射的資料平鋪為一個Observable

官方的圖解:

flatmap 圖例.png

到這裡我們總結一下:

  • flatmap 轉換是一對多的(一對一當然也支援),原來發射了幾個資料,轉換之後可以是更多個
  • flatMap 轉換同樣可以改變發射的資料型別
  • flatMap 轉換後的資料,還是會逐個發射給我們的Observer來接收(就像這些資料是由一個Observable發射的一樣,其實是多個Observable發射然後合併的)

這裡丟擲一個問題,flatMap會將原來的Observable,轉換為多個Observable來發射資料,那麼這些發射的資料是否會嚴格按順序發射然後被Observer接收

問題先留在這裡,進入原始碼

FlatMap 原始碼

FlatMap操作符涉及的程式碼會相對多一些,但是也是有規律可循。 同樣到Observable 類中看 flatMap的定義,原始碼作者為了方便開發者呼叫,提供了多個方法過載,我們最常用的方法定義如下

flatmap 方法1.png

最終呼叫的方法是

flatmap 方法.png

跟map 的套路 差不多,我們直接進入 ObservableFlatMap類, 我們還是看它的 subscribeActual 方法實現

ObservableFlatMap.png

可以看到,它給原Observer 裝飾後的 Observer 是 MergeObserver,我們再繼續看 MergeObserver 的 onNext 方法

MergeObserver onnext.png

由於我們預設呼叫的flatmap 的 maxConcurrency 大小是 Integer.MAX_VALUE, 所以最終會呼叫 subscribeInner(p),注意這裡我們的mapper方法以及被呼叫了,p就是跟我們傳入的Function生成的Observable,我們再繼續往下看

MergeObserver subscribeInner.png

一般我們傳入的Function 生成的Observable 都不是 Callable型別的,所以最終傳給Observable p 的 是InnerObserver, 找到了最終元凶,直接去看它的onNext方法實現吧。

MergeObserver onnext.png

funsionMode 預設是 None,走第一個if 邏輯,最終呼叫的是 上面的MergeObserable 的 tryEmit 方法,繼續進去看

MergeObservable tryEmit.png

這裡要插一句,MergeObserver 繼承了 AtomicInteger,所以這裡的tryEmit方法就利用了 AtomicInteger 的同步機制,所以同時只會有一個 value 被 actual Observer 發射,而且這裡 剛好 可以解答我們上面留下的 問題,由於 AtomicInteger CAS鎖只能保證操作的原子性,並不保證鎖的獲取順序,是搶佔式的,所以最終資料的發射順序並不是固定的(同一個Observable發出的資料是有序的)

如果沒有獲取到鎖,就會將要發射的資料放入 佇列中,drainLoop 方法會迴圈去獲取佇列中的 資料,然後發射,由於篇幅有限,更詳細的呼叫過程大家可以看原始碼。

dramloop.png

Map 和 FlatMap 二個操作符的 原始碼就解析到這裡,水平有限,有不對的,還望大佬不吝賜教。

相關文章