看完這篇blog,我相信你確定想當即在你的項目中使用RxJava了,這篇blog將介紹許多RxJava中的操做符,RxJava的強大性就來自於它所定義的操做符。
首先先看一個例子:編程
假設我有這樣一個方法:
這個方法根據輸入的字符串返回一個網站的url列表(啊哈,搜索引擎)併發
Observable<List<String>> query(String text);
如今我但願構建一個健壯系統,它能夠查詢字符串而且顯示結果。根據上一篇blog的內容,咱們可能會寫出下面的代碼:ide
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能夠大大簡化代碼長度:url
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的不少疑問也就一併解決了。spa
flatMap()實在不能更讚了,它能夠返回任何它想返回的Observable對象。
好比下面的方法:code
// 返回網站的標題,若是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的另一些很酷的特性,好比錯誤處理和併發,這些特性並不會直接用來處理數據。