在第一部分,我講解了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)載請注明出處,并保留此段聲明。