天天看點

RxJava系列三(轉換操作符)

RxJava系列三(轉換操作符)

前面兩篇文章中我們介紹了RxJava的一些基本概念和RxJava最簡單的用法。從這一章開始,我們開始聊聊RxJava中的操作符Operators,後面我将用三章的篇幅來分别介紹:

  1. 轉換類操作符
  2. 過濾類操作符
  3. 組合類操作符

這一章我們主要講講轉換類操作符。所有這些Operators都作用于一個可觀測序列,然後變換它發射的值,最後用一種新的形式傳回它們。概念實在是不好了解,下面我們結合實際的例子一一介紹。

Map

map(Func1)

函數接受一個Func1類型的參數(就像這樣

map(Func1<? super T, ? extends R> func)

),然後吧這個Func1應用到每一個由Observable發射的值上,将發射的隻轉換為我們期望的值。這種狗屁定義我相信你也聽不懂,我們來看一下官方給出的原理圖:

RxJava系列三(轉換操作符)

假設我們需要将一組數字裝換成字元串,我們可以通過map這樣實作:

Observable.just(1, 2, 3, 4, 5)
        .map(new Func1<Integer, String>() {

            @Override
            public String call(Integer i) {
                return "This is " + i;
            }
        }).subscribe(new Action1<String>() {
            @Override
            public void call(String s) {
                System.out.println(s);
            }
        });           

複制

Func1構造函數中的兩個參數分别是Observable發射值目前的類型和map轉換後的類型,上面這個例子中發射值目前的類型是Integer,轉換後的類型是String。

FlatMap

flatMap(Func1)

函數同樣也是做轉換的,但是作用卻不一樣。flatMap不太好了解,我們直接看例子(我們公司是個房産平台,那我就拿房子舉例):假設我們有一組小區資料

Community[] communites

,現在我們要輸出每個小區的名字;我們可以這樣實作:

Observable.from(communities)
        .map(new Func1<Community, String>() {

            @Override
            public String call(Community community) {
                return community.name;
            }
        })
        .subscribe(new Action1<String>() {
            @Override
            public void call(String name) {
                System.out.println("Community name : " + name);
            }
        });           

複制

現在我們需求有變化,需要列印出每個小區下面每一套房子的價格。于是我可以這樣實作:

Community[] communities = {};
Observable.from(communities)
        .subscribe(new Action1<Community>() {
            @Override
            public void call(Community community) {
                for (House house : community.houses) {
                    System.out.println("House price : " + house.price);
                }
            }
        });           

複制

複制

如果我不想在Subscriber中使用for循環,而是希望Subscriber中直接傳入單個的House對象呢?用map()顯然是不行的,因為map()是一對一的轉化,而我現在的要求是一對多的轉化。那麼我們可以使用flatMap()把一個Community轉化成多個House。

Observable.from(communities)
        .flatMap(new Func1<Community, Observable<House>>() {
            @Override
            public Observable<House> call(Community community) {
                return Observable.from(community.houses);
            }
        })
        .subscribe(new Action1<House>() {
            @Override
            public void call(House house) {
                System.out.println("House price : " + house.price);
            }
        });           

複制

複制

從前面的例子中我們發現,flatMap()和map()都是把傳入的參數轉化之後傳回另一個對象。但和map()不同的是,flatMap()中傳回的是Observable對象,并且這個Observable對象并不是被直接發送到 Subscriber的回調方法中。

flatMap(Func1)的原理是這樣的:

  1. 将傳入的事件對象裝換成一個Observable對象;
  2. 這是不會直接發送這個Observable, 而是将這個Observable激活讓它自己開始發送事件;
  3. 每一個建立出來的Observable發送的事件,都被彙入同一個Observable,這個Observable負責将這些事件統一交給Subscriber的回調方法。

這三個步驟,把事件拆成了兩級,通過一組新建立的Observable将初始的對象『鋪平』之後通過統一路徑分發了下去。而這個『鋪平』就是flatMap()所謂的flat。

最後我們來看看flatMap的原理圖:

RxJava系列三(轉換操作符)

ConcatMap

concatMap(Func1)

解決了

flatMap()

的交叉問題,它能夠把發射的值連續在一起,就像這樣:

RxJava系列三(轉換操作符)

flatMapIterable

flatMapIterable(Func1)

flatMap()

幾乎是一樣的,不同的是

flatMapIterable()

它轉化的多個Observable是使用Iterable作為源資料的。

RxJava系列三(轉換操作符)
Observable.from(communities)
        .flatMapIterable(new Func1<Community, Iterable<House>>() {
            @Override
            public Iterable<House> call(Community community) {
                return community.houses;
            }
        })
        .subscribe(new Action1<House>() {

            @Override
            public void call(House house) {

            }
        });           

複制

SwitchMap

switchMap(Func1)

flatMap(Func1)

很像,除了一點:每當源

Observable

發射一個新的資料項(Observable)時,它将取消訂閱并停止監視之前那個資料項産生的

Observable

,并開始監視目前發射的這一個。

RxJava系列三(轉換操作符)

Scan

scan(Func2)

對一個序列的資料應用一個函數,并将這個函數的結果發射出去作為下個資料應用合格函數時的第一個參數使用。

RxJava系列三(轉換操作符)

我們來看個簡單的例子:

Observable.just(1, 2, 3, 4, 5)
        .scan(new Func2<Integer, Integer, Integer>() {
            @Override
            public Integer call(Integer integer, Integer integer2) {
                return integer + integer2;
            }
        }).subscribe(new Action1<Integer>() {
    @Override
    public void call(Integer integer) {
        System.out.print(integer+“ ”);
    }
});           

複制

輸出結果為:

1 3 6 10 15             

複制

GroupBy

groupBy(Func1)

将原始Observable發射的資料按照key來拆分成一些小的Observable,然後這些小Observable分别發射其所包含的的資料,和SQL中的groupBy類似。實際使用中,我們需要提供一個生成key的規則(也就是Func1中的call方法),所有key相同的資料會包含在同一個小的Observable中。另外我們還可以提供一個函數來對這些資料進行轉化,有點類似于內建了flatMap。

RxJava系列三(轉換操作符)

單純的文字描述和圖檔解釋可能難以了解,我們來看個例子:假設我現在有一組房源

List<House> houses

,每套房子都屬于某一個小區,現在我們需要根據小區名來對房源進行分類,然後依次将房源資訊輸出。

List<House> houses = new ArrayList<>();
houses.add(new House("中糧·海景壹号", "中糧海景壹号新出大平層!總價4500W起"));
houses.add(new House("竹園新村", "滿五唯一,黃金地段"));
houses.add(new House("中糧·海景壹号", "毗鄰湯臣一品"));
houses.add(new House("竹園新村", "頂層戶型,兩室一廳"));
houses.add(new House("中糧·海景壹号", "南北通透,豪華五房"));
Observable<GroupedObservable<String, House>> groupByCommunityNameObservable = Observable.from(houses)
        .groupBy(new Func1<House, String>() {

            @Override
            public String call(House house) {
                return house.communityName;
            }
        });           

複制

通過上面的代碼我們建立了一個新的Observable:

groupByCommunityNameObservable

,它将會發送一個帶有

GroupedObservable

的序列(也就是指發送的資料項的類型為GroupedObservable)。

GroupedObservable

是一個特殊的

Observable

,它基于一個分組的key,在這個例子中的key就是小區名。現在我們需要将分類後的房源依次輸出:

Observable.concat(groupByCommunityNameObservable)
        .subscribe(new Action1<House>() {
            @Override
            public void call(House house) {
                System.out.println("小區:"+house.communityName+"; 房源描述:"+house.desc);
            }
        });           

複制

執行結果:

小區:中糧·海景壹号; 房源描述:中糧海景壹号新出大平層!總價4500W起
小區:中糧·海景壹号; 房源描述:毗鄰湯臣一品
小區:中糧·海景壹号; 房源描述:南北通透,豪華五房
小區:竹園新村; 房源描述:滿五唯一,黃金地段
小區:竹園新村; 房源描述:頂層戶型,兩室一廳           

複制

轉換類的操作符就先介紹到這,後續還會繼續介紹組合、過濾類的操作符及源碼分析,敬請期待!

如果大家喜歡這一系列的文章,歡迎關注我的知乎專欄和GitHub。
  • 知乎專欄:https://zhuanlan.zhihu.com/baron
  • GitHub:https://github.com/BaronZ88