理解RxJava:(二)Operator,Operator

第一部分,我講解了RxJava的基本結(jié)構(gòu),也介紹了map()操作。然而,我能理解你仍舊不會選擇使用Rxjava——你仍然還有很多東西沒有學(xué)到。但是這個(gè)情況將很快得到改變。Rxjava一大部分的能力是因?yàn)槠渲械膐perators。

讓我們通過一個(gè)例子來向你們介紹更多的operators。

初始

假設(shè)我有一個(gè)這樣的方法:

//返回一個(gè)基于文本查詢網(wǎng)站鏈接的列表
Observable<List<String>> query(String text); 

我想要構(gòu)建一個(gè)搜索文本和顯示結(jié)果的強(qiáng)健系統(tǒng)?;谏掀恼挛覀儗W(xué)到的,以下是我們馬上想到的:

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

這個(gè)答案讓人非常不滿意,因?yàn)槭チ宿D(zhuǎn)換數(shù)據(jù)流的能力。如果我想要修改每個(gè)URL,只能在每個(gè)Subscriber里面修改。這就違背了使用map()操作的初衷。

我可以為ulrs->urls創(chuàng)建一個(gè)map(),但是每個(gè)map()的內(nèi)部都有一個(gè)for-each循環(huán)。哎喲。

一線希望

有一個(gè)方法,Observable.from(),輸入一些items,然后每次發(fā)出一個(gè):

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

看起來有些幫助,讓我們看看:

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

沒有了for-each循環(huán),但是代碼顯得很混亂?,F(xiàn)在變成了多個(gè)嵌套的subscriptions了。除了代碼丑陋以及難以修改外,也違背了RxJava的一些原則。

更好的方法

屏住你的呼吸,因?yàn)槟阋姷搅四愕木仁乐鳎?code>flatMap()。

Observable.flatMap()獲取一個(gè)Observable的返回值,將值發(fā)給另一個(gè)取代它的Observable。如下:

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));

我寫成完整的方法是為了你能看到發(fā)生了什么,但是用 lambda表達(dá)式簡寫看起來很棒:

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

flatMap()(看起來)很怪,對嗎?為什么返回另一個(gè)Observable?核心概念是新的Observable返回的正是Subscriber所觀察的。它不接收List<String>——它接收Observable.from()返回的一系列的單獨(dú)的Strings。

此外

我強(qiáng)調(diào)這個(gè)觀點(diǎn)幾遍都不足夠:flatMap()能返回任意想要的Observable。

假設(shè)我又有一個(gè)這樣的方法:

// 返回網(wǎng)站的標(biāo)題,若是404則返回null
Observable<String> getTitle(String URL);

原本是打印URL,現(xiàn)在我想要打印接收的每個(gè)網(wǎng)站的標(biāo)題。但是有些問題:我的方法只對每次一個(gè)URL有效,而且它返回的不是String,它返回的是發(fā)出String的Observable。

有了flatMap(),解決這個(gè)問題很簡單。在把一系列的URL分開為單獨(dú)的items后,我可以在flatMap()方法中對于每個(gè)URL使用getTitle(),在它到達(dá)Subscriber前。

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));

同樣,使用lambda簡寫:

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

很酷,對吧?我把幾個(gè)返回Observable方法組合在一起。

不僅僅于此,我還將兩個(gè)API調(diào)用組合在一條方法鏈上了。你們知道維持所有的API調(diào)用同步,必須在數(shù)據(jù)展示前將它們的回調(diào)寫在一起,是有多痛苦?我們不用再忍受嵌套回調(diào)了。所有的邏輯都包在簡短的響應(yīng)式調(diào)用中了。

大量的Operators

到目前為止,我們僅僅學(xué)習(xí)了兩種operators。有很多還沒有學(xué)到。其他的operators能怎樣改善我們的代碼呢?

getTitle()在URL404的時(shí)候返回null。我們不想要輸出"null"。以下代碼顯示我們可以過濾掉null:

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

filter()方法發(fā)出和它們接收到的同樣的item,只在通過了boolean檢查的情況下。

現(xiàn)在我們只想要最多顯示5個(gè)結(jié)果:

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ù)量的item(如果少于5個(gè)標(biāo)題,它會提前停止)。

現(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()讓我們可以在每次一個(gè)item被發(fā)出之前,添加額外的行為。

看操作數(shù)據(jù)流多么簡單。你可以繼續(xù)對數(shù)據(jù)添加操作而不會弄糟任何事情。

RxJava有非常多的Operators。這么多operators讓我們被嚇到,但是值得查閱一遍以知道哪個(gè)對我們有用。消化這些操作會花費(fèi)點(diǎn)時(shí)間,但是我們能信手拈來的時(shí)候就能感受到Rxjava真正的強(qiáng)大。

以上都是官方提供的,我們甚至可以自定義operators!這超出了本文的討論范圍。但是只要你想你就能做到。

So What?

如果你是個(gè)懷疑論者。你會問為什么要關(guān)注這些operators?

關(guān)鍵點(diǎn)3 Operators讓你能對數(shù)據(jù)流做任何事

唯一的限制就是你自己。

你可以處理復(fù)雜的邏輯,從使用簡單的operators鏈開始。它將你的代碼打破為可重組的零碎東西。這就是函數(shù)響應(yīng)式編程。你用的越多,就越能改變你編程的思維。

另外,想想我們的代碼一轉(zhuǎn)換消費(fèi)起來變得多容易。最后的例子,我們調(diào)用了兩次API,操作數(shù)據(jù),然后存儲。但是Subscriber并不知道這些。它想的僅僅是消費(fèi)Observable<String>。封裝讓編程更簡單。

在第三部分,我們將繼續(xù)了解RxJava的特性。比如錯(cuò)誤處理和并發(fā),和操作數(shù)據(jù)沒有直接聯(lián)系。

本文翻譯自Grokking RxJava, Part 2: Operator Operator,著作權(quán)歸原作者danlew所有。譯文由JohnTsai翻譯。轉(zhuǎn)載請注明出處,并保留此段聲明。

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

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

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