- 原文鏈接: Don't break the chain: use RxJava's compose() operator
- 原文作者: Daniel Lew
- 譯文出自: 小鄧子的簡(jiǎn)書(shū)
- 譯者: 小鄧子
- 校對(duì)者: hi大頭鬼hi
- 狀態(tài): 完成
- 譯者注:為了方便因Lambda(譯文)還不夠了解的同學(xué)進(jìn)行閱讀,本篇譯文替換了原作中全部Lambda表達(dá)式。
RxJava的另一個(gè)好處在于,我們可以清楚地看到數(shù)據(jù)是如何在一系列操作符之間進(jìn)行轉(zhuǎn)換的。
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);
}
});
如何將一組操作符重用于多個(gè)數(shù)據(jù)流中呢?例如,因?yàn)橄M诠ぷ骶€程中處理數(shù)據(jù),然后在主線程中處理結(jié)果,所以我會(huì)頻繁使用subscribeOn()
和observeOn()
。如果我能夠通過(guò)重用的方式,將這種邏輯運(yùn)用到我所有的數(shù)據(jù)流中,將是一件多么偉大的事。
糟糕的實(shí)現(xiàn)方式
下面這些代碼是我在過(guò)去幾個(gè)月里一直都在使用的,正好可以拿來(lái)當(dāng)反面教材。
首先,使用schedulers
(調(diào)度)創(chuàng)建一個(gè)方法:
<T> Observable<T> applySchedulers(Observable<T> observable) {
return observable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
然后,封裝Observable
數(shù)據(jù)鏈:
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);
}
});
雖然這樣做也能達(dá)到目的,但是它看起來(lái)不僅丑,還容易讓人產(chǎn)生困惑,applySchedulers()
到底什么鬼?它不再符合操作符鏈路式結(jié)構(gòu),所以,看起來(lái)很難理解。然而,我找不到任何辦法去格式化這段代碼,因此,這并不尷尬。
現(xiàn)在,試想一下,如果在一個(gè)數(shù)據(jù)流中反復(fù)使用的話,這個(gè)反面教材將會(huì)變得要多爛有多爛。(譯者注:OMG)
Transformers簡(jiǎn)介
聰明的同學(xué)可能已經(jīng)意識(shí)到了這個(gè)問(wèn)題,但是RxJava早已提供了一種解決方案:Transformer(譯者注:有轉(zhuǎn)換器意思),一般情況下可以通過(guò)使用操作符Observable.compose()來(lái)實(shí)現(xiàn)。
Transformer
實(shí)際上就是一個(gè)Func1<Observable<T>, Observable<R>>
,換言之就是:可以通過(guò)它將一種類(lèi)型的Observable
轉(zhuǎn)換成另一種類(lèi)型的Observable
,和調(diào)用一系列的內(nèi)聯(lián)操作符是一模一樣的。
接下來(lái),我們一起創(chuàng)建一個(gè)Transformer
與schedulers
相結(jié)合的方法:
<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
表達(dá)式,會(huì)變得更加優(yōu)雅:
<T> Transformer<T, T> applySchedulers() {
return observable -> observable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
更新于2015年3月11日:如果使用 JDK 7或者更早版本進(jìn)行編譯的話,就不得不做出一些額外的改變,為compose()
添加泛型。顯式聲明返回類(lèi)型,就像這樣:
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
上一個(gè)實(shí)例中,我在所有的方法調(diào)用中都new
(譯者注:新建)了一個(gè)Transformer
實(shí)例。你可以創(chuàng)建一個(gè)實(shí)例化版本,節(jié)省不必要的實(shí)例化對(duì)象。畢竟,Transformers關(guān)乎著代碼重用。
如果,總是將一個(gè)具體的類(lèi)型轉(zhuǎn)換成另一個(gè)具體的類(lèi)型,那么可以很容易的創(chuàng)建一個(gè)Transformer
實(shí)例:
Transformer<String, String> myTransformer = new Transformer<String, String>() {
// ...Do your work here...
};
那么scheduler Transformer
(調(diào)度轉(zhuǎn)換器)應(yīng)該怎樣實(shí)現(xiàn)呢?因?yàn)?,它根本不需要關(guān)心類(lèi)型,所以就無(wú)法定義一個(gè)泛型實(shí)例:
// Doesn't compile; where would T come from?(譯者注:編譯無(wú)法通過(guò);因?yàn)楦静恢?T 從何而來(lái)。)
Transformer<T, T> myTransformer;
可能有人會(huì)這樣定義Transformer<Object, Object>
,然而這并沒(méi)有什么卵用,并且造成的后果就是Observable
將失去其類(lèi)型信息。
為了解決這個(gè)問(wèn)題,我從Collections中得到了一些啟發(fā),這個(gè)包裝類(lèi)有這樣一堆方法,能夠創(chuàng)建類(lèi)型安全并且不可變的空集合(比如,Collections.emptyList())。由于在其內(nèi)部使用了一個(gè)無(wú)泛型實(shí)例,所以需要封裝在一個(gè)添加泛型約束的方法里。
可以像下面這樣定義我們的schedulers Transformer
(調(diào)度轉(zhuǎn)換器)實(shí)例:
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;
}
現(xiàn)在我們終于把他做成一個(gè)“單例”了(譯者注:此單例非彼單例)。
警告:如果不做類(lèi)型轉(zhuǎn)換檢查,可能陷入麻煩。確保Transformer
真正與類(lèi)型無(wú)關(guān)。否則,即使代碼通過(guò)了編譯,在運(yùn)行時(shí)仍然存在拋出ClassCastException
異常的隱患。在當(dāng)前場(chǎng)景中,我們知道它是安全的,因?yàn)?code>schedulers(譯者注:調(diào)度)并不會(huì)與發(fā)送出的事件產(chǎn)生任何的互動(dòng)操作。
** flatMap()操作符怎么樣?**
現(xiàn)在你可能會(huì)好奇,compose()
操作符和flatMap()
操作符有何區(qū)別。他們最終都會(huì)發(fā)送出Observable<R>
,這就意味著,兩者都能夠用于操作符的重用?
不同點(diǎn)在于compose()
操作符擁有更高層次的抽象概念:它操作于整個(gè)數(shù)據(jù)流中,不僅僅是某一個(gè)被發(fā)送的事件。具體如下:
compose()
是唯一一個(gè)能夠從數(shù)據(jù)流中得到原始Observable<T>
的操作符,所以,那些需要對(duì)整個(gè)數(shù)據(jù)流產(chǎn)生作用的操作(比如,subscribeOn()
和observeOn()
)需要使用compose()
來(lái)實(shí)現(xiàn)。相較而言,如果在flatMap()
中使用subscribeOn()
或者observeOn()
,那么它僅僅對(duì)在(譯者注:深坑,會(huì)在后面的系列著重講解,歡迎關(guān)注)。flatMap()
中創(chuàng)建的Observable
起作用,而不會(huì)對(duì)剩下的流產(chǎn)生影響當(dāng)創(chuàng)建
Observable
流的時(shí)候,compose()
會(huì)立即執(zhí)行,猶如已經(jīng)提前寫(xiě)好了一個(gè)操作符一樣,而flatMap()
則是在onNext()
被調(diào)用后執(zhí)行,onNext()
的每一次調(diào)用都會(huì)觸發(fā)flatMap()
,也就是說(shuō),flatMap()
轉(zhuǎn)換每一個(gè)事件,而compose()
轉(zhuǎn)換的是整個(gè)數(shù)據(jù)流。因?yàn)槊恳淮握{(diào)用
onNext()
后,都不得不新建一個(gè)Observable
,所以flatMap()
的效率較低。事實(shí)上,compose()
操作符只在主干數(shù)據(jù)流上執(zhí)行操作。
如果想重用一些操作符,還是使用compose()
吧,雖然flatMap()
的用處很多,但作為重用代碼這一點(diǎn)來(lái)講,并不適用。