原文鏈接:Grokking RxJava, Part 2: Operator, Operator
在第一部分中我瀏覽了RxJava的基本結構,並且介紹了map操作符。我理解你仍舊沒十足的意願使用RxJava,因爲你目前只是瞭解的一點點。但是看過接下來這麼文章就會有所改變,RxJava框架的強大之處在於包含了大量的操作符。
讓我通過例子,介紹更多的操作符。
0x00 初始工作
假設有這樣一個方法,通過輸入內容獲取一個URLs列表:
// Returns a List of website URLs based on a text search
Observable<List<String>> query(String text);
我希望構建一個強大的系統,用於查詢輸入內容並顯示查詢結果。根據上一篇文章的知識,可能寫作這樣的代碼:
query("Hello, world!")
.subscribe(urls -> {
for (String url : urls) {
System.out.println(url);
}
});
這個作品完全不盡如人意,以至於失去了修改數據流的能力。如果我希望修改每一個URL,我不得不在Subscriber處理全部的列表。怎麼能忘記酷炫的map()操作符呢!
我加入map操作符,但是要處理的還是URLs列表,在內部還是不能擺脫for-each循環。
0x01 一線希望
有個一個方法Observable.from(),使用一組事件並且每次發射一個事件:
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循環,但是這份代碼是醜陋的,產生了多個嵌套的subscriptions ,除了醜陋和難已修改,還破壞了接下來還每一提到了RxJava的特性。
0x02 改進
救星來了,屏住呼吸:flatMap()
flatMap將一個發射數據的Observable變換爲都個Observables,然後將它們發射的數據合併後放在一個單獨的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?這裏的關鍵概念是,新的可觀察到的返回是Subscriber要看到的。Subscriber不再收到List,而是收到一系列單獨的字符串,就像Observable.from()。
這裏對應我來說,比較難以理解。但是一旦頓悟,就飛上雲霄。
0x03 還可以更好
不得不強調,flapMap()可以返回任何的Observable。
假設有下面這樣的方法:
// Returns the title of a website, or null if 404
Observable<String> getTitle(String URL);
現在我不想打印URLs列表,我希望打印一個網站的標題。但是遇到一個問題,這個方法之一次只接受一個URL,並且返回的是發射字符串的Observable,而不是字符串。
通過flatMap(),很容易解決這個問題:
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組合成一個Observables。太酷了!
不止於此,請注意我組合了兩個API的鏈式調用。當然,我可以組合任意數量的API調用。想想回調地獄吧,現在的邏輯多麼簡單。
0x04 豐富的操作符
目前,我們只接觸兩個操作符,還有很多沒有接觸的。我們怎麼樣改善我們的代碼呢?
如果訪問URL遇到404,getTitle()返回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()不改變發出的數據,只發射通過布爾檢查的數據
下面只想要顯示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()發出指定數量的事件。(在本例中,如果標題數量少於5個,Observable會提前停止)。
現在我們希望同時在磁盤上保存每一個結果:
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()允許我們在發出事件後添加額外的行爲,在本例就是保存標題。
你已經注意到操作數據流是多麼的容易!你可以加入更多的操作,程序還不會混亂。
RxJava包含大量的操作符。雖然多的嚇人,但是我們值得了解每一個操作符。一旦你知道操作符的使用場景,你就能將操作符轉化爲真正的力量。
在這些操作符之外,你甚至可以編寫自己的操作符。這已經超出本文的範圍,基本上只有你想不到的,沒有做不到的。
0x05 感覺怎樣?
如果這些還不能決心使用RxJava,那你又爲什麼關心每一個操作符呢?
主要理念3:操作符可以對數據流做任何事。
唯一的限制就在於你自己
你可以通過響應式簡化複雜的邏輯。RxJava可以複雜的程序分解爲可以組合的片段,這就是響應式編程的魅力。隨着你對RxJava的瞭解,你會爲它所折服。
在第三部分,我將介紹RxJava的其他特性,如錯誤處理和併發性。
另外,想想這多麼簡化了數據最終的消費形式。在例子最後,我通過兩個API調用,修改數據並保存在磁盤。但是Subscriber並不瞭解這些,它最終只消費Observable。封裝簡化編程。