1. 程式人生 > >RxJava2 轉換操作符之FlatMap()方法

RxJava2 轉換操作符之FlatMap()方法

開發十年,就只剩下這套架構體系了! >>>   

前言: 
本篇基於Map操作符,閱讀該篇請確保,你已經瞭解過它:RxJava2 轉換操作符之Map()方法。一如既往,幹大事的人都是直接先上圖,再解釋的。(建議用PC端閱讀,圖片可以放大。排版,也有利於閱讀)

二、FlatMap的操作流程圖解 
先來跟你講講,這圖咋看。這上面的線,表示原始資料。下面的線,表示轉換後的資料。中間的塊,代表轉換過程。兩條線的結尾有一小條豎線,表示結束。箭頭的方向,從左到右,表示執行過程,是從左到右跑的。 
 
1、(Question-1)你可能會奇怪,為毛從左到右數,第5個會是綠色的,不應該也是藍色的嗎? 
這個,得分成多步解釋,請耐心一下:

圓與稜形是一對多的關係。即一個圓既可以轉換成一個稜形,也可以轉換成多個稜形。該圖解,以每1個圓都對應2個稜形為例。那麼,可不可以第1個圓對應2個稜形,第2個圓對應3個稜形,第3個圓對應x個稜形呢?當然可以!
FlatMap的Flat是鋪平的意思。即先將所有圓轉成稜形,比如例子中,是先將3個圓轉成6個稜形。然後,再一 一開始對它們進行轉換,看圖(… 找個時間得學一下ps才行, 圖有點粗糙,但理不糙): 
 
雖然,是從左到右開始執行任務。但誰先執行完,誰就先返回。就跟跑步一樣,雖然有人先跑,但未必他就是先到終點的。
圓是一級任務,每一個一級任務包含多個二級子任務。子任務,即是稜形。
2、(Question-2)不是說,一個一個執行嗎?那怎麼會出現交叉(就是兩藍中插了個一綠的)呢? 
因為,不同的二級任務的執行時間是不同的。比方說:第一個綠色稜形的任務是計算:1+1。第二個綠色的稜形任務是:從伺服器獲取使用者資訊和系統配置。第一個藍色稜形的任務是計算:2+2。第二個藍色的稜形任務是:做特別複雜耗時的操作,比第二個綠色的稜形的任務執行的還要久。那麼,從例子可以看出。第一個綠色稜形肯定是先完成。但第二個綠色稜形的任務,執行的時間肯定比,第一個藍色稜形的任務還要久。所以,就會出現,先出現第一個藍色稜形,後再出現第二個綠色的稜形。。。

三、少跟我說理論!上程式碼,要複製就能跑,一眼就看得懂的。 
答:… 先喝點加多寶,我這就上程式碼。

    public static void actionFlatMap() {

        List<Integer> list = Arrays.asList(1, 2, 3);

        // 這裡用了lambda。
        Observable.fromIterable(list)
                .flatMap(integer -> {
                    log("開始執行,第" + integer + "圓球的任務" + getThreadName());
                    return getObservable(integer);
                }).subscribe(s -> log("已完成" + s + getThreadName()));

    }

    public static Observable<String> getObservable(final int integer) {
        return Observable.create((ObservableOnSubscribe<String>) emitter -> {
            emitter.onNext("第" + integer + "圓球的第1個稜形任務");
            if(integer != 1) {
                // 第2和第3個圓球的第二個任務延時。
                Thread.sleep(5 * 1000);
            }
            emitter.onNext("第" + integer + "圓球的第2個稜形任務");
            emitter.onComplete();
        }).subscribeOn(Schedulers.newThread());
    }

    // 返回當前的執行緒名
    public static String getThreadName() {
        return "  |  ThreadName=" + Thread.currentThread().getName();
    }

   private static void log(String log) {
        Log.d("FlatMap", log);
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
這是執行結果:

D/TransformingOperations: 開始執行,第1圓球的任務  |  ThreadName=main
D/TransformingOperations: 開始執行,第2圓球的任務  |  ThreadName=main
D/TransformingOperations: 開始執行,第3圓球的任務  |  ThreadName=main
D/TransformingOperations: 已完成第2圓球的第1個稜形任務  |  ThreadName=RxNewThreadScheduler-2
D/TransformingOperations: 已完成第3圓球的第1個稜形任務  |  ThreadName=RxNewThreadScheduler-3
D/TransformingOperations: 已完成第1圓球的第1個稜形任務  |  ThreadName=RxNewThreadScheduler-3
D/TransformingOperations: 已完成第1圓球的第2個稜形任務  |  ThreadName=RxNewThreadScheduler-3
D/TransformingOperations: 已完成第2圓球的第2個稜形任務  |  ThreadName=RxNewThreadScheduler-2
D/TransformingOperations: 已完成第3圓球的第2個稜形任務  |  ThreadName=RxNewThreadScheduler-2
1
2
3
4
5
6
7
8
9
從列印的結果可以看到,FlatMap,首先是從1到3,即從左到右,執行任務的。其中,1、2、3又各自包含2個子任務。雖然是從1到3開始執行。但是,很明顯,未必就是1先執行完畢。反而是,2、3的第一個任務先完成,然後,才是1的兩個任務完成。然後,才是2、3的兩個被延時處理的任務被分別完成。從這個例子,我們得出這樣一個結論:FlatMap執行流程是:先將所有一級任務,鋪平成所有二級任務。再依照,從左到右到執行次序,執行任務。但是,任務成功的回撥,卻不是從左到右的。而是,誰先完成誰先回調。簡言之,即:執行次序是一定的,完成次序是不確定的。

四、讓我們來看看Rx官方是怎麼解釋的

FlatMap,英文水平有限,如果翻譯有不當之處,歡迎各種建議。

1、transform the items emitted by an Observable into Observables, then flatten the emissions from those into a single Observable。

翻譯過來就是:由一個Observable發射一組任務,來將它們轉成多個Observable。然後,鋪平那些Observable到一個Observable裡面。聽起來是不是很難理解?因為這是硬翻譯,來看看軟翻譯怎麼說:

由一個Observable來觸發執行,一組轉換任務。做法是:先將這些任務以及它們的子任務,提出來。然後,再將這些任務合併到一個Observable裡面。最後,由這個Observable,對這些任務進行遍歷處理。。。如果還看不懂,沒關係,下面還有更詳細的解釋。

2、The FlatMap operator transforms an Observable by applying a function that you specify to each item emitted by the source Observable, where that function returns an Observable that itself emits items. FlatMap then merges the emissions of these resulting Observables, emitting these merged results as its own sequence.

This method is useful, for example, when you have an Observable that emits a series of items that themselves have Observable members or are in other ways transformable into Observables, so that you can create a new Observable that emits the complete collection of items emitted by the sub-Observables of these items.

Note that FlatMap merges the emissions of these Observables, so that they may interleave.

In several of the language-specific implementations there is also an operator that does not interleave the emissions from the transformed Observables, but instead emits these emissions in strict order, often called ConcatMap or something similar.

FlatMap操作符,轉換一個Observable的做法是,對每一個任務,都通過呼叫SourceObservable的方法來實現轉換。這個方法由你實現,具體的轉換邏輯,就在這個方法裡面處理。並且,該方法會返回一個Observable,這個Observable又可以處理它自己的任務。FlatMap會合並所有的任務,即將一級任務,先全部轉成二級任務,再遍歷處理。

這個方法是很有用的。比如說,當你的Observable執行一組任務,或處理一組資料的同時。這些任務或資料,又包含有自己的任務,或資料。所以,你可以為每一個任務,建立一個Observable來處理,它們的二級任務。

注意:因為FlatMap會合並所有的任務。所以,它們可能會有交叉現象。。這句話的意思是,因為將所有一級任務的二級任務都合併成一條線。然後,遍歷執行。這樣,有的任務,可能會因耗時而慢回撥。從而導致,先執行,後回撥的現象。

最後一段,簡而言之:如果,你既想實現這個功能,又不想出現交叉。即每一個任務,都會等前一個任務,執行完,再回調。可以用ConcatMap操作符。下篇,我們會講:ConcatMap操作符和FlatMapIterable操作符。

到此為止,FlatMap操作符的講解,已經全部說完了。如果,你還有什麼疑惑不解,可以在評論區指出。

附上可以跑的程式碼(0 warnings, 0 errors): 
https://github.com/SuperBeagleDog/OkMVP

注意: 
這個庫裡面有很多東西,RxJava2的轉換操作符部分的demo位於:com.lyf.okmvp.demo.rxjava2包下的TransformingOperations類裡面。

用法: 
1、直接在com.lyf.okmvp.ui包下的MainActivity類裡的onCreate()方法裡面,直接呼叫:

TransformingOperations.actionFlatMap(); // 靜態方法。

2、也可以複製TransformingOperations類到你的專案裡,去為所欲為。但前提時,你得有配置過RxJava和RxAndroid。配置方法,自行百度,或參考我的OkMVP庫。
--------------------- 
作者:嶽鋒 
來源:CSDN 
原文:https://blog.csdn.net/l_o_s/article/details/79412300 
版權宣告:本文為博主原創文章,轉載請