- 原文鏈接: Don't break the chain: use RxJava's compose() operator
- 原文作者: Daniel Lew
- 譯文出自: 小鄧子的簡書
- 譯者: 小鄧子
- 校對者: hi大頭鬼hi
- 狀態: 完成
- 譯者注:為了方便因Lambda(譯文)還不夠了解的同學進行閱讀,本篇譯文替換了原作中全部Lambda表達式。
RxJava的另一個好處在于,我們可以清楚地看到數據是如何在一系列操作符之間進行轉換的。
Observable.from(someSource)
.map(new Func1<Data, Data>() {
@Override public Data call(Data data) {
return manipulate(data);
}
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Data>() {
@Override public void call(Data data) {
doSomething(data);
}
});
如何將一組操作符重用于多個數據流中呢?例如,因為希望在工作線程中處理數據,然后在主線程中處理結果,所以我會頻繁使用subscribeOn()
和observeOn()
。如果我能夠通過重用的方式,將這種邏輯運用到我所有的數據流中,將是一件多么偉大的事。
糟糕的實現方式
下面這些代碼是我在過去幾個月里一直都在使用的,正好可以拿來當反面教材。
首先,使用schedulers
(調度)創建一個方法:
<T> Observable<T> applySchedulers(Observable<T> observable) {
return observable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
然后,封裝Observable
數據鏈:
applySchedulers(Observable.from(someSource).map(new Func1<Data, Data>() {
@Override public Data call(Data data) {
return manipulate(data);
}
})
).subscribe(new Action1<Data>() {
@Override public void call(Data data) {
doSomething(data);
}
});
雖然這樣做也能達到目的,但是它看起來不僅丑,還容易讓人產生困惑,applySchedulers()
到底什么鬼?它不再符合操作符鏈路式結構,所以,看起來很難理解。然而,我找不到任何辦法去格式化這段代碼,因此,這并不尷尬。
現在,試想一下,如果在一個數據流中反復使用的話,這個反面教材將會變得要多爛有多爛。(譯者注:OMG)
Transformers簡介
聰明的同學可能已經意識到了這個問題,但是RxJava早已提供了一種解決方案:Transformer(譯者注:有轉換器意思),一般情況下可以通過使用操作符Observable.compose()來實現。
Transformer
實際上就是一個Func1<Observable<T>, Observable<R>>
,換言之就是:可以通過它將一種類型的Observable
轉換成另一種類型的Observable
,和調用一系列的內聯操作符是一模一樣的。
接下來,我們一起創建一個Transformer
與schedulers
相結合的方法:
<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
表達式,會變得更加優雅:
<T> Transformer<T, T> applySchedulers() {
return observable -> observable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
更新于2015年3月11日:如果使用 JDK 7或者更早版本進行編譯的話,就不得不做出一些額外的改變,為compose()
添加泛型。顯式聲明返回類型,就像這樣:
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
實例:
Transformer<String, String> myTransformer = new Transformer<String, String>() {
// ...Do your work here...
};
那么scheduler Transformer
(調度轉換器)應該怎樣實現呢?因為,它根本不需要關心類型,所以就無法定義一個泛型實例:
// Doesn't compile; where would T come from?(譯者注:編譯無法通過;因為根本不知道 T 從何而來。)
Transformer<T, T> myTransformer;
可能有人會這樣定義Transformer<Object, Object>
,然而這并沒有什么卵用,并且造成的后果就是Observable
將失去其類型信息。
為了解決這個問題,我從Collections中得到了一些啟發,這個包裝類有這樣一堆方法,能夠創建類型安全并且不可變的空集合(比如,Collections.emptyList())。由于在其內部使用了一個無泛型實例,所以需要封裝在一個添加泛型約束的方法里。
可以像下面這樣定義我們的schedulers Transformer
(調度轉換器)實例:
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<R>
,這就意味著,兩者都能夠用于操作符的重用?
不同點在于compose()
操作符擁有更高層次的抽象概念:它操作于整個數據流中,不僅僅是某一個被發送的事件。具體如下:
compose()
是唯一一個能夠從數據流中得到原始Observable<T>
的操作符,所以,那些需要對整個數據流產生作用的操作(比如,subscribeOn()
和observeOn()
)需要使用compose()
來實現。相較而言,如果在flatMap()
中使用subscribeOn()
或者observeOn()
,那么它僅僅對在(譯者注:深坑,會在后面的系列著重講解,歡迎關注)。flatMap()
中創建的Observable
起作用,而不會對剩下的流產生影響當創建
Observable
流的時候,compose()
會立即執行,猶如已經提前寫好了一個操作符一樣,而flatMap()
則是在onNext()
被調用后執行,onNext()
的每一次調用都會觸發flatMap()
,也就是說,flatMap()
轉換每一個事件,而compose()
轉換的是整個數據流。因為每一次調用
onNext()
后,都不得不新建一個Observable
,所以flatMap()
的效率較低。事實上,compose()
操作符只在主干數據流上執行操作。
如果想重用一些操作符,還是使用compose()
吧,雖然flatMap()
的用處很多,但作為重用代碼這一點來講,并不適用。