在第一部分,我講解了RxJava的基本結構,也介紹了map()
操作。然而,我能理解你仍舊不會選擇使用Rxjava——你仍然還有很多東西沒有學到。但是這個情況將很快得到改變。Rxjava一大部分的能力是因為其中的operators。
讓我們通過一個例子來向你們介紹更多的operators。
初始
假設我有一個這樣的方法:
//返回一個基于文本查詢網站鏈接的列表
Observable<List<String>> query(String text);
我想要構建一個搜索文本和顯示結果的強健系統?;谏掀恼挛覀儗W到的,以下是我們馬上想到的:
query("Hello, world!")
.subscribe(urls -> {
for (String url : urls) {
System.out.println(url);
}
});
這個答案讓人非常不滿意,因為失去了轉換數據流的能力。如果我想要修改每個URL,只能在每個Subscriber
里面修改。這就違背了使用map()
操作的初衷。
我可以為ulrs->urls創建一個map()
,但是每個map()
的內部都有一個for-each循環。哎喲。
一線希望
有一個方法,Observable.from()
,輸入一些items,然后每次發出一個:
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循環,但是代碼顯得很混亂?,F在變成了多個嵌套的subscriptions了。除了代碼丑陋以及難以修改外,也違背了RxJava的一些原則。
更好的方法
屏住你的呼吸,因為你見到了你的救世主:flatMap()
。
Observable.flatMap()
獲取一個Observable
的返回值,將值發給另一個取代它的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));
我寫成完整的方法是為了你能看到發生了什么,但是用 lambda表達式簡寫看起來很棒:
query("Hello, world!")
.flatMap(urls -> Observable.from(urls))
.subscribe(url -> System.out.println(url));
flatMap()
(看起來)很怪,對嗎?為什么返回另一個Observable
?核心概念是新的Observable
返回的正是Subscriber
所觀察的。它不接收List<String>
——它接收Observable.from()
返回的一系列的單獨的Strings
。
此外
我強調這個觀點幾遍都不足夠:flatMap()
能返回任意想要的Observable
。
假設我又有一個這樣的方法:
// 返回網站的標題,若是404則返回null
Observable<String> getTitle(String URL);
原本是打印URL,現在我想要打印接收的每個網站的標題。但是有些問題:我的方法只對每次一個URL有效,而且它返回的不是String,它返回的是發出String的Observable
。
有了flatMap()
,解決這個問題很簡單。在把一系列的URL分開為單獨的items后,我可以在flatMap()
方法中對于每個URL使用getTitle()
,在它到達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));
很酷,對吧?我把幾個返回Observable
方法組合在一起。
不僅僅于此,我還將兩個API調用組合在一條方法鏈上了。你們知道維持所有的API調用同步,必須在數據展示前將它們的回調寫在一起,是有多痛苦?我們不用再忍受嵌套回調了。所有的邏輯都包在簡短的響應式調用中了。
大量的Operators
到目前為止,我們僅僅學習了兩種operators。有很多還沒有學到。其他的operators能怎樣改善我們的代碼呢?
getTitle()
在URL404的時候返回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()
方法發出和它們接收到的同樣的item,只在通過了boolean檢查的情況下。
現在我們只想要最多顯示5個結果:
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()
最多發出指定數量的item(如果少于5個標題,它會提前停止)。
現在我們想要存儲每個標題到磁盤上:
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()
讓我們可以在每次一個item被發出之前,添加額外的行為。
看操作數據流多么簡單。你可以繼續對數據添加操作而不會弄糟任何事情。
RxJava有非常多的Operators。這么多operators讓我們被嚇到,但是值得查閱一遍以知道哪個對我們有用。消化這些操作會花費點時間,但是我們能信手拈來的時候就能感受到Rxjava真正的強大。
以上都是官方提供的,我們甚至可以自定義operators!這超出了本文的討論范圍。但是只要你想你就能做到。
So What?
如果你是個懷疑論者。你會問為什么要關注這些operators?
關鍵點3 Operators讓你能對數據流做任何事
唯一的限制就是你自己。
你可以處理復雜的邏輯,從使用簡單的operators鏈開始。它將你的代碼打破為可重組的零碎東西。這就是函數響應式編程。你用的越多,就越能改變你編程的思維。
另外,想想我們的代碼一轉換消費起來變得多容易。最后的例子,我們調用了兩次API,操作數據,然后存儲。但是Subscriber
并不知道這些。它想的僅僅是消費Observable<String>
。封裝讓編程更簡單。
在第三部分,我們將繼續了解RxJava的特性。比如錯誤處理和并發,和操作數據沒有直接聯系。
本文翻譯自Grokking RxJava, Part 2: Operator Operator,著作權歸原作者danlew所有。譯文由JohnTsai翻譯。轉載請注明出處,并保留此段聲明。