首先先看一個例子:
準備工作
假設我有這樣一個方法:這個方法根據輸入的字符串返回一個網站的url列表(啊哈,搜索引擎)
- Observable<List<String>> query(String text);
- query("Hello, world!")
- .subscribe(urls -> {
- for (String url : urls) {
- System.out.println(url);
- }
- });
當然,我可以使用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));
- });
改進
救星來了,他就是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));
- query("Hello, world!")
- .flatMap(urls -> Observable.from(urls))
- .subscribe(url -> System.out.println(url));
這部分也是我當初學RxJava的時候最難理解的部分,一旦我突然領悟了,RxJava的很多疑問也就一併解決了。
還可以更好
flatMap()實在不能更讚了,它可以返回任何它想返回的Observable對象。比如下面的方法:
- // 返回網站的標題,如果404了就返回null
- Observable<String> getTitle(String URL);
- 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));
- query("Hello, world!")
- .flatMap(urls -> Observable.from(urls))
- .flatMap(url -> getTitle(url))
- .subscribe(title -> System.out.println(title));
不止這些,我還將兩個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));
如果我們只想要最多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));
如果我們想在打印之前,把每個標題保存到磁盤:
- 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));
看到這裏操作數據流是多麼簡單了麼。你可以添加任意多的操作,並且不會搞亂你的代碼。
RxJava包含了大量的操作符。操作符的數量是有點嚇人,但是很值得你去挨個看一下,這樣你可以知道有哪些操作符可以使用。弄懂這些操作符可能會花一些時間,但是一旦弄懂了,你就完全掌握了RxJava的威力。
你甚至可以編寫自定義的操作符!這篇blog不打算將自定義操作符,如果你想的話,清自行Google吧。
感覺如何?
好吧,你是一個懷疑主義者,並且還很難被說服,那爲什麼你要關心這些操作符呢?因爲操作符可以讓你對數據流做任何操作。
將一系列的操作符鏈接起來就可以完成複雜的邏輯。代碼被分解成一系列可以組合的片段。這就是響應式函數編程的魅力。用的越多,就會越多的改變你的編程思維。
另外,RxJava也使我們處理數據的方式變得更簡單。在最後一個例子裏,我們調用了兩個API,對API返回的數據進行了處理,然後保存到磁盤。但是我們的Subscriber並不知道這些,它只是認爲自己在接收一個Observable<String>對象。良好的封裝性也帶來了編碼的便利!
在第三部分中,我將介紹RxJava的另外一些很酷的特性,比如錯誤處理和併發,這些特性並不會直接用來處理數據。