深入淺出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的另外一些很酷的特性,比如錯誤處理和并發,這些特性并不會直接用來處理數據。
原文鏈接

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,923評論 6 535
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,740評論 3 420
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,856評論 0 380
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,175評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,931評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,321評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,383評論 3 443
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,533評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,082評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,891評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,067評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,618評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,319評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,732評論 0 27
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,987評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,794評論 3 394
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,076評論 2 375

推薦閱讀更多精彩內容