- 原文鏈接: Deferring Observable code until subscription in RxJava
- 原文作者: Daniel Lew
- 譯文出自: 小鄧子的簡書
- 譯者: 小鄧子
- 校對者: hi大頭鬼hi
- 狀態(tài): 完成
- 譯者注:為了方便因Lambda(譯文)還不夠了解的同學(xué)進(jìn)行閱讀,本篇譯文替換了原作中全部Lambda表達(dá)式被芳。
我越來越喜歡把RxJava的defer()操作符作為一個工具來使用,以確保Observable
代碼在被訂閱后才執(zhí)行(而不是創(chuàng)建后立即執(zhí)行)。我之前寫過一些有關(guān)defer()的代碼袁滥,但是惜犀,現(xiàn)在我想做更詳細(xì)的描述不从。
假設(shè)藻丢,有個數(shù)據(jù)類:
public class SomeType {
private String value;
public void setValue(String value) {
this.value = value;
}
public Observable<String> valueObservable() {
return Observable.just(value);
}
}
這段代碼在運(yùn)行后會打印出什么呢把曼?
SomeType instance = new SomeType();
Observable<String> value = instance.valueObservable();
instance.setValue("Some Value");
value.subscribe(System.out::println);
如果你認(rèn)為會打印出“Some Value”杨帽,那就錯了。而實(shí)際打印結(jié)果是“null”祝迂。因?yàn)樵谡{(diào)用Observable.just()
的時候睦尽,value
已經(jīng)初始化了。
just()
型雳,from()
這類能夠創(chuàng)建Observable
的操作符(譯者注:創(chuàng)建Observable的操作符)在創(chuàng)建之初当凡,就已經(jīng)存儲了對象的值,而不被訂閱的時候纠俭。這種情況沿量,顯然不是預(yù)期表現(xiàn),我想要的valueObservable()
是無論什么時候請求冤荆,都能夠表現(xiàn)為當(dāng)前值朴则。
自助
一個解決辦法就是使用Observable.create()
,因?yàn)樗试S為每個訂閱者精確控制事件的發(fā)送钓简。
public Observable<String> valueObservable() {
return Observable.create(new Observable.OnSubscribe<String>() {
@Override public void call(Subscriber<? super String> subscriber) {
subscriber.onNext(value);
subscriber.onCompleted();
}
});
}
現(xiàn)在乌妒,valueObservable()
將在訂閱的時候發(fā)送當(dāng)前值(事件)。它除了在訂閱的時候才獲取value
(而不是創(chuàng)建的時候)之外外邓,看起來和Observable.just()
所做的沒什么兩樣撤蚊。
現(xiàn)在唯一的問題是,自從閱讀Dávid Karnok的解讀操作符系列文章后(譯者注:簡直不能更優(yōu)秀损话,一定要看)侦啸,我一直小心翼翼的編寫著自定義的操作符(譯者注:原著的意思是指,自定義操作符內(nèi)部處理方式丧枪,如上面代碼中的subscriber.onNext(value)
等)光涂。通過閱讀該系列,我發(fā)現(xiàn)很難寫出正確的操作符拧烦。來看看這篇文章忘闻,Observable.just()
為了支持背壓(譯者注:例如Observable.zip()操作符)和退訂是如何做出改變的。
當(dāng)然屎篱,上面那段代碼是能正確運(yùn)行的服赎,至少現(xiàn)在看來它是OK噠,但是隨著RxJava版本的不斷迭代交播,鬼知道以后能不能重虑。而且我也不知道類似背壓和退訂等操作能否安全的向下兼容。更何況秦士,我又不是操作符開發(fā)專家缺厉。所以,我試著避免自定義操作符,除非萬不得已提针。
簡單粗暴
這里有一種不需要自定義操作符的實(shí)現(xiàn)方式:
public Observable<String> valueObservable() {
return Observable.defer(new Func0<Observable<String>>() {
@Override public Observable<String> call() {
return Observable.just(value);
}
});
}
我所做的就是用defer()
操作符封裝原始代碼命爬,但現(xiàn)在的表現(xiàn)正是我想要的。defer()
中的代碼直到被訂閱才會執(zhí)行辐脖。我們只需要在請求數(shù)據(jù)的時候調(diào)用Observable.just()
就哦了饲宛。
我更喜歡這個解決方案的原因:
比
Observable.create()
更簡單,不再需要手動調(diào)用onCompleted()
嗜价。使用內(nèi)置操作符艇抠,這種方式(可能)更得到官方的肯定。
使用defer()
操作符的唯一缺點(diǎn)就是久锥,每次訂閱都會創(chuàng)建一個新的Observable
對象家淤。create()
操作符則為每一個訂閱者都使用同一個函數(shù),所以瑟由,后者效率更高絮重。一如既往地,如果有必要可以親測性能或者嘗試優(yōu)化歹苦。
深入
上面代碼僅僅是為講解所用青伤,但是,切換到實(shí)際生產(chǎn)中殴瘦,我們需要用BehaviorSubject
替換所有代碼潮模。讓我們來看一些更復(fù)雜的東西。
假設(shè)需要一個方法痴施,首先將數(shù)據(jù)寫進(jìn)磁盤,然后再作為結(jié)果返回究流。這是一種用defer()
操作符的實(shí)現(xiàn):
public Observable<SomeType> createSomeType(final String value) {
return Observable.defer(new Func0<Observable<SomeType>>() {
@Override public Observable<SomeType> call() {
SomeType someType = new SomeType();
someType.setValue(value);
try {
db.writeToDisk(someType);
} catch (IOException e) {
return Observable.error(e);
}
return Observable.just(someType);
}
});
}
這個例子稍微復(fù)雜一些辣吃,將數(shù)據(jù)寫進(jìn)磁盤的同時如果拋出異常并捕獲,則立即調(diào)用onError
芬探,基本的思路是相同的神得,那就是:在訂閱發(fā)生之前,不希望執(zhí)行任何代碼偷仿。
其實(shí)哩簿,有很多方式可以解決上面的問題,雖然使用defer()
操作符只是其中之一酝静,但是节榜,使用起來真的很方便。