Rx第五章

Rx第五章

轉(zhuǎn)換Observables

在上一章中挥下,我們探索了RxJava通用過濾方法趴泌。我們學(xué)習(xí)了如何使用filter()方法過濾我們不需要的值堡牡,如何使用take()得到發(fā)射元素的子集午绳,如何使用distinct()函數(shù)來去除重復(fù)的筒溃。我們學(xué)習(xí)了如何借助timeout()马篮,sample(),以及debounce()來利用時(shí)間怜奖。

這一章中浑测,我們將通過學(xué)習(xí)如何變換可觀測(cè)序列來創(chuàng)建一個(gè)能夠更好的滿足我們需求的序列。

*map家族

RxJava提供了幾個(gè)mapping函數(shù):map(),flatMap(),concatMap(),flatMapIterable()以及switchMap().所有這些函數(shù)都作用于一個(gè)可觀測(cè)序列歪玲,然后變換它發(fā)射的值迁央,最后用一種新的形式返回它們。讓我們用合適的“真實(shí)世界”的例子一個(gè)個(gè)的學(xué)習(xí)下滥崩。

Map

RxJava的map函數(shù)接收一個(gè)指定的Func對(duì)象然后將它應(yīng)用到每一個(gè)由Observable發(fā)射的值上岖圈。下圖展示了如何將一個(gè)乘法函數(shù)應(yīng)用到每個(gè)發(fā)出的值上以此創(chuàng)建一個(gè)新的Observable來發(fā)射轉(zhuǎn)換的數(shù)據(jù)。

考慮下我們已安裝的應(yīng)用列表钙皮。我們?cè)趺床拍軌蝻@示同樣的列表蜂科,但是又要所有的名字都小寫呢?

我們的loadList()函數(shù)可以改成這樣:

private void loadList(List<AppInfo> apps) {
    mRecyclerView.setVisibility(View.VISIBLE);
    Observable.from(apps)
        .map(new Func1<AppInfo,AppInfo>(){
            @Override
            public Appinfo call(AppInfo appInfo){
                String currentName = appInfo.getName();
                String lowerCaseName = currentName.toLowerCase();
                appInfo.setName(lowerCaseName);
                return appInfo;
            }
        })
        .subscribe(new Observer<AppInfo>() {

            @Override
            public void onCompleted() {
                mSwipeRefreshLayout.setRefreshing(false);
            }

            @Override
            public void onError(Throwable e) {
                Toast.makeText(getActivity(), "Something went wrong!", Toast.LENGTH_SHORT).show();
                mSwipeRefreshLayout.setRefreshing(false);
            }

            @Override
            public void onNext(AppInfo appInfo) {
                mAddedApps.add(appInfo); 
                mAdapter.addApplication(mAddedApps.size() - 1,appInfo);
            }
        });
}

正如你看到的短条,像往常一樣創(chuàng)建我們發(fā)射的Observable之后导匣,我們追加一個(gè)map調(diào)用,我們創(chuàng)建一個(gè)簡(jiǎn)單的函數(shù)來更新AppInfo對(duì)象并提供一個(gè)名字小寫的新版本給觀察者慌烧。

FlatMap

在復(fù)雜的場(chǎng)景中逐抑,我們有一個(gè)這樣的Observable:它發(fā)射一個(gè)數(shù)據(jù)序列,這些數(shù)據(jù)本身也可以發(fā)射Observable屹蚊。RxJava的flatMap()函數(shù)提供一種鋪平序列的方式厕氨,然后合并這些Observables發(fā)射的數(shù)據(jù),最后將合并后的結(jié)果作為最終的Observable汹粤。

當(dāng)我們?cè)谔幚砜赡苡写罅康腛bservables時(shí)命斧,重要是記住任何一個(gè)Observables發(fā)生錯(cuò)誤的情況,flatMap()將會(huì)觸發(fā)它自己的onError()函數(shù)并放棄整個(gè)鏈嘱兼。

重要的一點(diǎn)提示是關(guān)于合并部分:它允許交叉国葬。正如上圖所示,這意味著flatMap()不能夠保證在最終生成的Observable中源Observables確切的發(fā)射順序芹壕。

ConcatMap

RxJava的concatMap()函數(shù)解決了flatMap()的交叉問題汇四,提供了一種能夠把發(fā)射的值連續(xù)在一起的鋪平函數(shù),而不是合并它們踢涌,如下圖所示:

FlatMapIterable

作為*map家族的一員通孽,flatMapInterable()flatMap()很像。僅有的本質(zhì)不同是它將源數(shù)據(jù)兩兩結(jié)成對(duì)并生成Iterable睁壁,而不是原始數(shù)據(jù)項(xiàng)和生成的Observables背苦。

SwitchMap

如下圖所示互捌,switchMap()flatMap()很像,除了一點(diǎn):每當(dāng)源Observable發(fā)射一個(gè)新的數(shù)據(jù)項(xiàng)(Observable)時(shí)行剂,它將取消訂閱并停止監(jiān)視之前那個(gè)數(shù)據(jù)項(xiàng)產(chǎn)生的Observable秕噪,并開始監(jiān)視當(dāng)前發(fā)射的這一個(gè)。

Scan

RxJava的scan()函數(shù)可以看做是一個(gè)累積函數(shù)厚宰。scan()函數(shù)對(duì)原始Observable發(fā)射的每一項(xiàng)數(shù)據(jù)都應(yīng)用一個(gè)函數(shù)腌巾,計(jì)算出函數(shù)的結(jié)果值,并將該值填充回可觀測(cè)序列固阁,等待和下一次發(fā)射的數(shù)據(jù)一起使用壤躲。

作為一個(gè)通用的例子,給出一個(gè)累加器:

Observable.just(1,2,3,4,5)
        .scan((sum,item) -> sum + item)
        .subscribe(new Subscriber<Integer>() {
            @Override
            public void onCompleted() {
                Log.d("RXJAVA", "Sequence completed.");
            }

            @Override
            public void onError(Throwable e) {
                Log.e("RXJAVA", "Something went south!");
            }

            @Override
            public void onNext(Integer item) {
                Log.d("RXJAVA", "item is: " + item);
            }
        });

我們得到的結(jié)果是:

RXJAVA: item is: 1
RXJAVA: item is: 3
RXJAVA: item is: 6
RXJAVA: item is: 10
RXJAVA: item is: 15
RXJAVA: Sequence completed.

我們也可以創(chuàng)建一個(gè)新版本的loadList()函數(shù)用來比較每個(gè)安裝應(yīng)用的名字從而創(chuàng)建一個(gè)名字長(zhǎng)度遞增的列表备燃。

private void loadList(List<AppInfo> apps) {
    mRecyclerView.setVisibility(View.VISIBLE);
    Observable.from(apps)
            .scan((appInfo,appInfo2) -> {
                if(appInfo.getName().length > appInfo2.getName().length()){
                    return appInfo;
                } else {
                    return appInfo2;
                }
            })
            .distinct()
            .subscribe(new Observer<AppInfo>() {

                @Override
                public void onCompleted() {
                    mSwipeRefreshLayout.setRefreshing(false);
                }

                @Override
                public void onError(Throwable e) {
                    Toast.makeText(getActivity(), "Something went wrong!", Toast.LENGTH_SHORT).show();
                    mSwipeRefreshLayout.setRefreshing(false);
                }

                @Override
                public void onNext(AppInfo appInfo) {
                    mAddedApps.add(appInfo); 
                    mAdapter.addApplication(mAddedApps.size() - 1,appInfo);
                }
            });
}

結(jié)果如下:

有一個(gè)scan()函數(shù)的變體,它用初始值作為第一個(gè)發(fā)射的值凌唬,方法簽名看起來像:scan(R,Func2)并齐,如下圖中的例子這樣:

GroupBy

拿第一個(gè)例子開始,我們安裝的應(yīng)用程序列表按照字母表的順序排序客税。然而况褪,如果現(xiàn)在我們想按照最近更新日期來排序我們的App時(shí)該怎么辦?RxJava提供了一個(gè)有用的函數(shù)從列表中按照指定的規(guī)則:groupBy()來分組元素更耻。下圖中的例子展示了groupBy()如何將發(fā)射的值根據(jù)他們的形狀來進(jìn)行分組测垛。

這個(gè)函數(shù)將源Observable變換成一個(gè)發(fā)射Observables的新的Observable。它們中的每一個(gè)新的Observable都發(fā)射一組指定的數(shù)據(jù)秧均。

為了創(chuàng)建一個(gè)分組了的已安裝應(yīng)用列表食侮,我們?cè)?code>loadList()函數(shù)中引入了一個(gè)新的元素:

Observable<GroupedObservable<String,AppInfo>> groupedItems = Observable.from(apps)
    .groupBy(new Func1<AppInfo,String>(){
        @Override
        public String call(AppInfo appInfo){
            SimpleDateFormat formatter = new SimpleDateFormat("MM/yyyy");
            return formatter.format(new Date(appInfo.getLastUpdateTime()));
        }
    });

現(xiàn)在我們創(chuàng)建了一個(gè)新的Observable,groupedItems目胡,它將會(huì)發(fā)射一個(gè)帶有GroupedObservable的序列锯七。GroupedObservable是一個(gè)特殊的Observable,它源自一個(gè)分組的key誉己。在這個(gè)例子中眉尸,key就是String,代表的意思是Month/Year格式化的最近更新日期巨双。

這一點(diǎn)噪猾,我們已經(jīng)創(chuàng)建了幾個(gè)發(fā)射AppInfo數(shù)據(jù)的Observable,用來填充我們的列表筑累。我們想保留字母排序和分組排序袱蜡。我們將創(chuàng)建一個(gè)新的Observable將所有的聯(lián)系起來,像往常一樣然后訂閱它:

Observable.concat(groupedItems)
    .subscribe(new Observer<AppInfo>() {

        @Override
        public void onCompleted() {
            mSwipeRefreshLayout.setRefreshing(false);
        }

        @Override
        public void onError(Throwable e) {
            Toast.makeText(getActivity(), "Something went wrong!", Toast.LENGTH_SHORT).show();
            mSwipeRefreshLayout.setRefreshing(false);
        }

        @Override
        public void onNext(AppInfo appInfo) {
            mAddedApps.add(appInfo); 
            mAdapter.addApplication(mAddedApps.size() - 1,appInfo);
        }
    });

我們的loadList()函數(shù)完成了疼阔,結(jié)果是:

Buffer

RxJava中的buffer()函數(shù)將源Observable變換一個(gè)新的Observable戒劫,這個(gè)新的Observable每次發(fā)射一組列表值而不是一個(gè)一個(gè)發(fā)射半夷。

上圖中展示了buffer()如何將count作為一個(gè)參數(shù)來指定有多少數(shù)據(jù)項(xiàng)被包在發(fā)射的列表中。實(shí)際上迅细,buffer()函數(shù)有幾種變體巫橄。其中有一個(gè)是允許你指定一個(gè)skip值:此后每skip項(xiàng)數(shù)據(jù),然后又用count項(xiàng)數(shù)據(jù)填充緩沖區(qū)茵典。如下圖所示:

buffer()帶一個(gè)timespan的參數(shù)湘换,會(huì)創(chuàng)建一個(gè)每隔timespan時(shí)間段就會(huì)發(fā)射一個(gè)列表的Observable。

Window

RxJava的window()函數(shù)和buffer()很像统阿,但是它發(fā)射的是Observable而不是列表彩倚。下圖展示了window()如何緩存3個(gè)數(shù)據(jù)項(xiàng)并把它們作為一個(gè)新的Observable發(fā)射出去。

這些Observables中的每一個(gè)都發(fā)射原始Observable數(shù)據(jù)的一個(gè)子集扶平,數(shù)量由count指定,最后發(fā)射一個(gè)onCompleted()結(jié)束帆离。正如buffer()一樣,window()也有一個(gè)skip變體,如下圖所示:

Cast

RxJava的cast()函數(shù)是本章中最后一個(gè)操作符。它是map()操作符的特殊版本结澄。它將源Observable中的每一項(xiàng)數(shù)據(jù)都轉(zhuǎn)換為新的類型哥谷,把它變成了不同的Class

總結(jié)

這一章中麻献,我們學(xué)習(xí)了RxJava時(shí)如何控制和轉(zhuǎn)換可觀測(cè)序列们妥。用我們現(xiàn)在所學(xué)的知識(shí),我們可以創(chuàng)建勉吻、過濾监婶、轉(zhuǎn)換我們所想要的任何種類的可觀測(cè)序列。

下一章齿桃,我們將學(xué)習(xí)如何組合Observable惑惶,合并它們,連接它們源譬,再或者打包它們集惋。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市踩娘,隨后出現(xiàn)的幾起案子刮刑,更是在濱河造成了極大的恐慌,老刑警劉巖养渴,帶你破解...
    沈念sama閱讀 212,718評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件雷绢,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡理卑,警方通過查閱死者的電腦和手機(jī)翘紊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來藐唠,“玉大人帆疟,你說我怎么就攤上這事鹉究。” “怎么了踪宠?”我有些...
    開封第一講書人閱讀 158,207評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵自赔,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我柳琢,道長(zhǎng)绍妨,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,755評(píng)論 1 284
  • 正文 為了忘掉前任柬脸,我火速辦了婚禮他去,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘倒堕。我一直安慰自己灾测,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,862評(píng)論 6 386
  • 文/花漫 我一把揭開白布涩馆。 她就那樣靜靜地躺著行施,像睡著了一般。 火紅的嫁衣襯著肌膚如雪魂那。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,050評(píng)論 1 291
  • 那天稠项,我揣著相機(jī)與錄音涯雅,去河邊找鬼。 笑死展运,一個(gè)胖子當(dāng)著我的面吹牛活逆,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播拗胜,決...
    沈念sama閱讀 39,136評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼蔗候,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了埂软?” 一聲冷哼從身側(cè)響起锈遥,我...
    開封第一講書人閱讀 37,882評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎勘畔,沒想到半個(gè)月后炒嘲,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蛇券,經(jīng)...
    沈念sama閱讀 44,330評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,651評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蒜绽。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,789評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡秀撇,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出抡秆,到底是詐尸還是另有隱情,我是刑警寧澤吟策,帶...
    沈念sama閱讀 34,477評(píng)論 4 333
  • 正文 年R本政府宣布儒士,位于F島的核電站,受9級(jí)特大地震影響踊挠,放射性物質(zhì)發(fā)生泄漏乍桂。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,135評(píng)論 3 317
  • 文/蒙蒙 一效床、第九天 我趴在偏房一處隱蔽的房頂上張望睹酌。 院中可真熱鬧,春花似錦剩檀、人聲如沸憋沿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽辐啄。三九已至,卻和暖如春运嗜,著一層夾襖步出監(jiān)牢的瞬間壶辜,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評(píng)論 1 267
  • 我被黑心中介騙來泰國(guó)打工担租, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留砸民,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,598評(píng)論 2 362
  • 正文 我出身青樓奋救,卻偏偏與公主長(zhǎng)得像岭参,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子尝艘,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,697評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容

  • 本篇文章介主要紹RxJava中操作符是以函數(shù)作為基本單位演侯,與響應(yīng)式編程作為結(jié)合使用的,對(duì)什么是操作背亥、操作符都有哪些...
    嘎啦果安卓獸閱讀 2,851評(píng)論 0 10
  • 響應(yīng)式編程簡(jiǎn)介 響應(yīng)式編程是一種基于異步數(shù)據(jù)流概念的編程模式秒际。數(shù)據(jù)流就像一條河:它可以被觀測(cè),被過濾隘梨,被操作程癌,或者...
    說碼解字閱讀 3,058評(píng)論 0 5
  • 作者: maplejaw本篇只解析標(biāo)準(zhǔn)包中的操作符。對(duì)于擴(kuò)展包轴猎,由于使用率較低嵌莉,如有需求,請(qǐng)讀者自行查閱文檔捻脖。 創(chuàng)...
    maplejaw_閱讀 45,636評(píng)論 8 93
  • 創(chuàng)建操作 用于創(chuàng)建Observable的操作符Create通過調(diào)用觀察者的方法從頭創(chuàng)建一個(gè)ObservableEm...
    rkua閱讀 1,816評(píng)論 0 1
  • 1锐峭,text field(文本框)的onKeyUp事件中鼠,實(shí)現(xiàn)每輸入一個(gè)字,觸發(fā)一次事件 (輸入框用textFile...
    y煙雨任平生閱讀 296評(píng)論 0 0