深入淺出RxJava(二:操作符)
文章轉自:
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0309/2571.html
在第一篇中,我介紹了RxJava的一些基礎知識,同時也介紹了map()操作符。當然如果你並沒有意願去使用RxJava我一點都不詫異,畢竟才接觸了這麼點。看完這篇blog,我相信你肯定想立即在你的專案中使用RxJava了,這篇blog將介紹許多RxJava中的操作符,RxJava的強大性就來自於它所定義的操作符。首先先看一個例子:準備工作
假設我有這樣一個方法:這個方法根據輸入的字串返回一個網站的url列表(啊哈,搜尋引擎)
Observable<List<String>> query(String text);
現在我希望構建一個健壯系統,它可以查詢字串並且顯示結果。根據上一篇blog的內容,我們可能會寫出下面的程式碼:
query("Hello, world!")
.subscribe(urls -> {
for (String url : urls) {
System.out.println(url);
}
});
這種程式碼當然是不能容忍的,因為上面的程式碼使我們喪失了變化資料流的能力。一旦我們想要更改每一個URL,只能在Subscriber中來做。我們竟然沒有使用如此酷的map()操作符!!!當然,我可以使用map操作符,map的輸入是urls列表,處理的時候還是要for each遍歷,一樣很蛋疼。萬幸,還有Observable.from()方法,它接收一個集合作為輸入,然後每次輸出一個元素給subscriber:
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迴圈,但是程式碼依然看起來很亂。多個巢狀的subscription不僅看起來很醜,難以修改,更嚴重的是它會破壞某些我們現在還沒有講到的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呢?理解flatMap的關鍵點在於,flatMap輸出的新的Observable正是我們在Subscriber想要接收的。現在Subscriber不再收到List<String>,而是收到一些列單個的字串,就像Observable.from()的輸出一樣。這部分也是我當初學RxJava的時候最難理解的部分,一旦我突然領悟了,RxJava的很多疑問也就一併解決了。還可以更好
flatMap()實在不能更讚了,它可以返回任何它想返回的Observable物件。比如下面的方法:
// 返回網站的標題,如果404了就返回null
Observable<String> getTitle(String URL);
接著前面的例子,現在我不想列印URL了,而是要列印收到的每個網站的標題。問題來了,我的方法每次只能傳入一個URL,並且返回值不是一個String,而是一個輸出String的Observabl物件。使用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物件的方法組合在一起!帥呆了!不止這些,我還將兩個API的呼叫組合到一個鏈式呼叫中了。我們可以將任意多個API呼叫連結起來。大家應該都應該知道同步所有的API呼叫,然後將所有API呼叫的回撥結果組合成需要展示的資料是一件多麼蛋疼的事情。這裡我們成功的避免了callback hell(多層巢狀的回撥,導致程式碼難以閱讀維護)。現在所有的邏輯都包裝成了這種簡單的響應式呼叫。豐富的操作符
目前為止,我們已經接觸了兩個操作符,RxJava中還有更多的操作符,那麼我們如何使用其他的操作符來改進我們的程式碼呢?getTitle()返回null如果url不存在。我們不想輸出"null",那麼我們可以從返回的title列表中過濾掉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()輸出最多指定數量的結果。如果我們想在列印之前,把每個標題儲存到磁碟:
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包含了大量的操作符。操作符的數量是有點嚇人,但是很值得你去挨個看一下,這樣你可以知道有哪些操作符可以使用。弄懂這些操作符可能會花一些時間,但是一旦弄懂了,你就完全掌握了RxJava的威力。你甚至可以編寫自定義的操作符!這篇blog不打算將自定義操作符,如果你想的話,清自行Google吧。感覺如何?
好吧,你是一個懷疑主義者,並且還很難被說服,那為什麼你要關心這些操作符呢?因為操作符可以讓你對資料流做任何操作。將一系列的操作符連結起來就可以完成複雜的邏輯。程式碼被分解成一系列可以組合的片段。這就是響應式函式程式設計的魅力。用的越多,就會越多的改變你的程式設計思維。
另外,RxJava也使我們處理資料的方式變得更簡單。在最後一個例子裡,我們呼叫了兩個API,對API返回的資料進行了處理,然後儲存到磁碟。但是我們的Subscriber並不知道這些,它只是認為自己在接收一個Observable<String>物件。良好的封裝性也帶來了編碼的便利!
在第三部分中,我將介紹RxJava的另外一些很酷的特性,比如錯誤處理和併發,這些特性並不會直接用來處理資料。
原文連結
相關文章
- 深入淺出RxJava(2):操作符RxJava
- 深入淺出RxJava(一:基礎篇)RxJava
- 深入淺出RxJava(1):基礎篇RxJava
- RxJava 操作符系列二RxJava
- RxJava操作符系列二RxJava
- Rxjava2操作符(二)RxJava
- RxJava+Retrofit+OkHttp深入淺出-mvp(使用篇)RxJavaHTTPMVP
- Android RxJava使用介紹(二) RxJava的操作符AndroidRxJava
- 深入淺出RxJava(三)--響應式的好處RxJava
- 深入淺出RxJava(3):響應式的好處RxJava
- RxJava+Retrofit+OkHttp 深入淺出-終極封裝一RxJavaHTTP封裝
- Tomcat深入淺出——Servlet(二)TomcatServlet
- PostgreSQL VACUUM 之深入淺出 (二)SQL
- 深入淺出UML類圖(二)
- ActiveX深入淺出(二) (轉)
- RxJava 和 RxAndroid 二(操作符的使用)RxJavaAndroid
- 深入淺出FE(十四)深入淺出websocketWeb
- RxJava常用操作符RxJava
- RxJava操作符列表RxJava
- 深入淺出學習決策樹(二)
- 深入淺出之正規表示式(二)
- RxJava2原始碼分析(二):操作符原理分析RxJava原始碼
- Hive 內建操作符與函式開發——深入淺出學HiveHive函式
- RxJava操作符之建立操作符(三)RxJava
- 3章 RxJava操作符RxJava
- RxJava操作符系列一RxJava
- RxJava操作符系列三RxJava
- RxJava 操作符系列五RxJava
- RxJava操作符系列四RxJava
- 深入淺出 Runtime(二):資料結構資料結構
- 深入淺出Websocket(二)分散式Websocket叢集Web分散式
- 《Java專欄》 課題二:Java深入淺出Java
- 深入RxJava2 原始碼解析(二)RxJava原始碼
- RxJava操作符之組合操作符(六)RxJava
- RxJava操作符之轉換操作符(四)RxJava
- RxJava操作符之過濾操作符(五)RxJava
- RxJava+Retrofit+OkHttp深入淺出-終極封裝三(檔案上傳)RxJavaHTTP封裝
- RxJava+Retrofit+OkHttp深入淺出-終極封裝五(資料持久化)RxJavaHTTP封裝持久化