RxJava的另一個好處在於,我們可以清楚地看到資料是如何在一系列操作符之間進行轉換的。
1 2 3 4 5 6 7 8 9 10 11 12 |
Observable.from(someSource) .map(new Func1() { @Override public Data call(Data data) { return manipulate(data); } }).subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Action1() { @Override public void call(Data data) { doSomething(data); } }); |
如何將一組操作符重用於多個資料流中呢?例如,因為希望在工作執行緒中處理資料,然後在主執行緒中處理結果,所以我會頻繁使用subscribeOn()
和observeOn()
。如果我能夠通過重用的方式,將這種邏輯運用到我所有的資料流中,將是一件多麼偉大的事。
糟糕的實現方式
下面這些程式碼是我在過去幾個月裡一直都在使用的,正好可以拿來當反面教材。
首先,使用schedulers
(排程)建立一個方法:
1 2 3 4 |
<T> Observable<T> applySchedulers(Observable<T> observable) { return observable.subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()); } |
然後,封裝Observable
資料鏈:
1 2 3 4 5 6 7 8 9 10 |
applySchedulers(Observable.from(someSource).map(new Func1() { @Override public Data call(Data data) { return manipulate(data); } }) ).subscribe(new Action1() { @Override public void call(Data data) { doSomething(data); } }); |
雖然這樣做也能達到目的,但是它看起來不僅醜,還容易讓人產生困惑,applySchedulers()
到底什麼鬼?它不再符合操作符鏈路式結構,所以,看起來很難理解。然而,我找不到任何辦法去格式化這段程式碼,因此,這並不尷尬。
現在,試想一下,如果在一個資料流中反覆使用的話,這個反面教材將會變得要多爛有多爛。(譯者注:OMG)
Transformers簡介
聰明的同學可能已經意識到了這個問題,但是RxJava早已提供了一種解決方案:Transformer(譯者注:有轉換器意思),一般情況下可以通過使用操作符Observable.compose()來實現。
Transformer
實際上就是一個Func1, Observable>
,換言之就是:可以通過它將一種型別的Observable
轉換成另一種型別的Observable
,和呼叫一系列的內聯操作符是一模一樣的。
接下來,我們一起建立一個Transformer
與schedulers
相結合的方法:
1 2 3 4 5 6 7 8 9 |
<T> Transformer<T, T> applySchedulers() { return new Transformer<T, T>() { @Override public Observable<T> call(Observable<T> observable) { return observable.subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()); } }; } |
使用Lambda
表示式,會變得更加優雅:
1 2 3 4 |
<T> Transformer<T, T> applySchedulers() { return observable -> observable.subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()); } |
更新於2015年3月11日:如果使用 JDK 7或者更早版本進行編譯的話,就不得不做出一些額外的改變,為compose()
新增泛型。顯式宣告返回型別,就像這樣:
1 2 3 4 5 6 7 8 9 10 11 12 |
Observable.from(someSource) .map(new Func1<Data, Data>() { @Override public Data call(Data data) { return manipulate(data); } }) .compose(this.<YourType>applySchedulers()) .subscribe(new Action1<Data>() { @Override public void call(Data data) { doSomething(data); } }); |
重用Transformers
上一個例項中,我在所有的方法呼叫中都new
(譯者注:新建)了一個Transformer
例項。你可以建立一個例項化版本,節省不必要的例項化物件。畢竟,Transformers關乎著程式碼重用。
如果,總是將一個具體的型別轉換成另一個具體的型別,那麼可以很容易的建立一個Transformer
例項:
1 2 3 |
Transformer<String, String> myTransformer = new Transformer<String, String>() { // ...Do your work here... }; |
那麼scheduler Transformer
(排程轉換器)應該怎樣實現呢?因為,它根本不需要關心型別,所以就無法定義一個泛型例項:
1 2 |
// Doesn't compile; where would T come from?(譯者注:編譯無法通過;因為根本不知道 T 從何而來。) Transformer<T, T> myTransformer; |
可能有人會這樣定義Transformer
,然而這並沒有什麼卵用,並且造成的後果就是Observable
將失去其型別資訊。
為了解決這個問題,我從Collections中得到了一些啟發,這個包裝類有這樣一堆方法,能夠建立型別安全並且不可變的空集合(比如,Collections.emptyList())。由於在其內部使用了一個無泛型例項,所以需要封裝在一個新增泛型約束的方法裡。
可以像下面這樣定義我們的schedulers Transformer
(排程轉換器)例項:
1 2 3 4 5 6 7 8 9 10 |
final Observable.Transformer schedulersTransformer = new Observable.Transformer() { @Override public Object call(Object observable) { return ((Observable) observable).subscribeOn(Schedulers.newThread()) .observeOn(AndroidSchedulers.mainThread()); } }; <T> Observable.Transformer<T, T> applySchedulers() { return (Observable.Transformer<T, T>) schedulersTransformer; } |
現在我們終於把他做成一個“單例”了(譯者注:此單例非彼單例)。
警告:如果不做型別轉換檢查,可能陷入麻煩。確保Transformer
真正與型別無關。否則,即使程式碼通過了編譯,在執行時仍然存在丟擲ClassCastException
異常的隱患。在當前場景中,我們知道它是安全的,因為schedulers
(譯者注:排程)並不會與傳送出的事件產生任何的互動操作。
flatMap()操作符怎麼樣?
現在你可能會好奇,compose()
操作符和flatMap()
操作符有何區別。他們最終都會傳送出Observable
,這就意味著,兩者都能夠用於操作符的重用?
不同點在於compose()
操作符擁有更高層次的抽象概念:它操作於整個資料流中,不僅僅是某一個被髮送的事件。具體如下:
compose()
是唯一一個能夠從資料流中得到原始Observable
的操作符,所以,那些需要對整個資料流產生作用的操作(比如,subscribeOn()
和observeOn()
)需要使用compose()
來實現。相較而言,如果在flatMap()
中使用subscribeOn()
或者observeOn()
,那麼它僅僅對在(譯者注:深坑,會在後面的系列著重講解,歡迎關注)。flatMap()
中建立的Observable
起作用,而不會對剩下的流產生影響- 當建立
Observable
流的時候,compose()
會立即執行,猶如已經提前寫好了一個操作符一樣,而flatMap()
則是在onNext()
被呼叫後執行,onNext()
的每一次呼叫都會觸發flatMap()
,也就是說,flatMap()
轉換每一個事件,而compose()
轉換的是整個資料流。 - 因為每一次呼叫
onNext()
後,都不得不新建一個Observable
,所以flatMap()
的效率較低。事實上,compose()
操作符只在主幹資料流上執行操作。
如果想重用一些操作符,還是使用compose()
吧,雖然flatMap()
的用處很多,但作為重用程式碼這一點來講,並不適用。