運(yùn)用RxJava(二)操作符

原文鏈接,翻譯:葛藤灣


在本系列文章的第一部分我講述了RxJava的基本結(jié)構(gòu),也向你介紹了map()操作符。但是,我能夠理解你還并不能夠使用它——你所掌握的還沒(méi)有到能夠使用它的程度。這種狀況即將改變——RxJava大部分的能力都來(lái)自于這個(gè)框架中的操作符。

舉個(gè)例子來(lái)介紹它的操作符吧。

栗子

假定我現(xiàn)在有這樣一個(gè)方法:

// Returns a List of website URLs based on a text search
Observable<List<String>> query(String text); 

我想開(kāi)發(fā)一個(gè)能夠搜索文本并展示結(jié)果的系統(tǒng),根據(jù)上一篇文章我們所學(xué)到的,可以寫(xiě)出如下代碼:

query("Hello, world!")
    .subscribe(urls -> {
        for (String url : urls) {
            System.out.println(url);
        }
    });

這個(gè)答案自然恨不能讓人滿足,因?yàn)槲揖痛藛适Я俗儞Q數(shù)據(jù)流的能力。如果我想修改每一個(gè) url,我就必須在Subscriber中完成這一切。我們也就此拋棄了使用map()的技巧。

希望之光

有一個(gè)Observable.from()的方法,能夠?qū)魅氲募喜鸱殖删唧w的對(duì)象依次發(fā)送出來(lái):

Observable.from("url1", "url2", "url3")
    .subscribe(url -> System.out.println(url));

看起來(lái)像是有用的,那就看看發(fā)生了什么吧。

query("Hello, world!")
    .subscribe(urls -> {
        Observable.from(urls)
            .subscribe(url -> System.out.println(url));
    });

我拿到了一個(gè)foreach循環(huán),可結(jié)果代碼卻是一團(tuán)糟。我現(xiàn)在到手的是多重的嵌入的訂閱,除了丑陋、難以修改外,它還破壞了RxJava的一些重要的(盡管我們還不知曉)特征。我去!

更好的方法

屏氣凝神迎接你的救星吧:flatMap()。

Observable.flatMap()傳入一個(gè)Observable發(fā)送的數(shù)據(jù),返回另一個(gè)Observable發(fā)送的數(shù)據(jù)。突如其來(lái)的變化:你以為你得到的是一個(gè)數(shù)據(jù)流其實(shí)卻是另外一個(gè)。看看它是怎么解決這個(gè)問(wèn)題的:

query("Hello, world!")
    .flatMap(new Func1<List<String>, Observable<String>>() {
        @Override
        public Observable<String> call(List<String> urls) {
            return Observable.from(urls);
        }
    })
    .subscribe(url -> System.out.println(url));

上面是完整的函數(shù),這樣你就能確切地知道到底發(fā)送了什么,但如果用lambdas來(lái)簡(jiǎn)化代碼的話,看起來(lái)就很清爽了:

query("Hello, world!")
    .flatMap(urls -> Observable.from(urls))
    .subscribe(url -> System.out.println(url));

flatMap()有點(diǎn)奇怪吧?它為什么會(huì)返回另外一個(gè)Observable呢?這里的關(guān)鍵點(diǎn)是Subscriber看到的就是返回的新的Observable。它并沒(méi)有收到一個(gè)List<String>——它收到的是Observable.from()返回的一系列單獨(dú)的String。

還可以更好

我必須著重強(qiáng)調(diào):flatMap()能夠返回任意它想返回的Observable。

假定現(xiàn)在我又有一個(gè)方法:

// Returns the title of a website, or null if 404
Observable<String> getTitle(String URL);

現(xiàn)在我想打印得到的每一個(gè)網(wǎng)站的標(biāo)題而不再是URL,但有幾個(gè)問(wèn)題:我的方法同時(shí)只能作用在單獨(dú)的URL上,并且它并不返回一個(gè)String,而是返回一個(gè)發(fā)送著String的Observable。

使用flatMap()來(lái)解決這個(gè)問(wèn)題其實(shí)很簡(jiǎn)單,將URL列表分割完成之后,在URL到達(dá)Subscriber之前,我可以在flatMap()中對(duì)每個(gè)URL使用getTitle():

query("Hello, world!")
    .flatMap(urls -> Observable.from(urls))
    .flatMap(new Func1<String, Observable<String>>() {
        @Override
        public Observable<String> call(String url) {
            return getTitle(url);
        }
    })
    .subscribe(title -> System.out.println(title));

再一次使用lambdas簡(jiǎn)化代碼:

query("Hello, world!")
    .flatMap(urls -> Observable.from(urls))
    .flatMap(url -> getTitle(url))
    .subscribe(title -> System.out.println(title));

牛吧?我將多個(gè)單獨(dú)返回Observable的方法組合在了一起。爽!

還不止如此,注意我是怎么把兩個(gè)API調(diào)用合并到一個(gè)鏈?zhǔn)秸{(diào)用中的。這可以擴(kuò)展到任意多個(gè)API調(diào)用。你知道保證API調(diào)用同步有多痛苦嗎?但我們已經(jīng)跳過(guò)了回調(diào)的地獄,所有這些的相同邏輯都被封裝在了這個(gè)簡(jiǎn)短的reactive調(diào)用當(dāng)中。

豐富的操作符

到現(xiàn)在為止我們也就學(xué)了兩個(gè)操作符,但其實(shí)它還有很多。那么,我們還能怎么優(yōu)化我們的代碼呢?

getTitle()將返回null如果URL遭遇404錯(cuò)誤。但我們不想輸出“null”,事實(shí)證明我們可以將它過(guò)濾掉:

query("Hello, world!")
    .flatMap(urls -> Observable.from(urls))
    .flatMap(url -> getTitle(url))
    .filter(title -> title != null)
    .subscribe(title -> System.out.println(title));

filter()原樣發(fā)送收到的通過(guò)boolean檢查的數(shù)據(jù)。

現(xiàn)在我們一次最多只想顯示5條數(shù)據(jù):

query("Hello, world!")
    .flatMap(urls -> Observable.from(urls))
    .flatMap(url -> getTitle(url))
    .filter(title -> title != null)
    .take(5)
    .subscribe(title -> System.out.println(title));

take()一次最多發(fā)送指定的數(shù)據(jù)數(shù)目(如果少于5條就提前結(jié)束)。

現(xiàn)在,我們希望將每個(gè)標(biāo)題存盤:

query("Hello, world!")
    .flatMap(urls -> Observable.from(urls))
    .flatMap(url -> getTitle(url))
    .filter(title -> title != null)
    .take(5)
    .doOnNext(title -> saveTitle(title))
    .subscribe(title -> System.out.println(title));

doOnNext()允許我們?cè)跀?shù)據(jù)被發(fā)送之前添加額外的行為,這個(gè)例子中就是將標(biāo)題存盤。

瞧瞧操作數(shù)據(jù)流有多么簡(jiǎn)單吧。你可以給你的程序添加更多技巧但不會(huì)引起混亂。

RxJava有著豐富的操作符,這么多的操作符的確有些嚇人但值得復(fù)習(xí)一下,這樣你就知道哪些有用。消化這些操作符需要一些時(shí)間,但是一旦你這么做了,你手上就有了洪荒之力。

根據(jù)上面提供的那些,你甚至還可以自定義自己的操作符。那超出了本文的范圍,但只要你想做,你一定可以做到的。

然后呢?

好吧,看來(lái)你是一個(gè)很難滿足的懷疑論者。為什么你應(yīng)該關(guān)注所有這些操作符?

關(guān)鍵概念 #3: 操作符能夠讓你對(duì)數(shù)據(jù)流做任何事!

唯一的限制就是你自己

你能夠只使用簡(jiǎn)單的操作符鏈就建立起復(fù)雜的邏輯。它能夠?qū)⒛愕拇a分解成可組合的片段。那就是功能性的reactive編程。你使用得越多,你思考程序的方式改變得就會(huì)越多。

另外,試想一下,要去消費(fèi)我們變換完成后的數(shù)據(jù)是多么的簡(jiǎn)單。在這個(gè)例子的最后我們使用了兩個(gè)API調(diào)用,操作了數(shù)據(jù)然后存盤,但我們的Subscriber卻并不知道那些,它只會(huì)認(rèn)為它消費(fèi)了一個(gè)簡(jiǎn)單的Observable<String>。封裝,使得編程更為簡(jiǎn)易!

在本文的第三部分,我們將學(xué)到RxJava中不直接涉及操作數(shù)據(jù)的一些其他特征。比如錯(cuò)誤處理和并發(fā)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 本篇文章介主要紹RxJava中操作符是以函數(shù)作為基本單位,與響應(yīng)式編程作為結(jié)合使用的,對(duì)什么是操作、操作符都有哪些...
    嘎啦果安卓獸閱讀 2,987評(píng)論 0 10
  • 作者: maplejaw本篇只解析標(biāo)準(zhǔn)包中的操作符。對(duì)于擴(kuò)展包,由于使用率較低,如有需求,請(qǐng)讀者自行查閱文檔。 創(chuàng)...
    maplejaw_閱讀 46,210評(píng)論 8 93
  • 作者寄語(yǔ) 很久之前就想寫(xiě)一個(gè)專題,專寫(xiě)Android開(kāi)發(fā)框架,專題的名字叫 XXX 從入門到放棄 ,沉淀了這么久,...
    戴定康閱讀 7,735評(píng)論 13 85
  • http://blog.csdn.net/yyh352091626/article/details/5330472...
    奈何心善閱讀 3,651評(píng)論 0 0
  • 我從去年開(kāi)始使用 RxJava ,到現(xiàn)在一年多了。今年加入了 Flipboard 后,看到 Flipboard 的...
    Jason_andy閱讀 5,763評(píng)論 7 62

友情鏈接更多精彩內(nèi)容