- 原文鏈接: Deferring Observable code until subscription in RxJava
- 原文作者: Daniel Lew
- 譯文出自: 小鄧子的簡書
- 譯者: 小鄧子
- 校對者: hi大頭鬼hi
- 狀態: 完成
- 譯者注:為了方便因Lambda(譯文)還不夠了解的同學進行閱讀,本篇譯文替換了原作中全部Lambda表達式。
我越來越喜歡把RxJava的defer()操作符作為一個工具來使用,以確保Observable
代碼在被訂閱后才執行(而不是創建后立即執行)。我之前寫過一些有關defer()的代碼,但是,現在我想做更詳細的描述。
假設,有個數據類:
public class SomeType {
private String value;
public void setValue(String value) {
this.value = value;
}
public Observable<String> valueObservable() {
return Observable.just(value);
}
}
這段代碼在運行后會打印出什么呢?
SomeType instance = new SomeType();
Observable<String> value = instance.valueObservable();
instance.setValue("Some Value");
value.subscribe(System.out::println);
如果你認為會打印出“Some Value”,那就錯了。而實際打印結果是“null”。因為在調用Observable.just()
的時候,value
已經初始化了。
just()
,from()
這類能夠創建Observable
的操作符(譯者注:創建Observable的操作符)在創建之初,就已經存儲了對象的值,而不被訂閱的時候。這種情況,顯然不是預期表現,我想要的valueObservable()
是無論什么時候請求,都能夠表現為當前值。
自助
一個解決辦法就是使用Observable.create()
,因為它允許為每個訂閱者精確控制事件的發送。
public Observable<String> valueObservable() {
return Observable.create(new Observable.OnSubscribe<String>() {
@Override public void call(Subscriber<? super String> subscriber) {
subscriber.onNext(value);
subscriber.onCompleted();
}
});
}
現在,valueObservable()
將在訂閱的時候發送當前值(事件)。它除了在訂閱的時候才獲取value
(而不是創建的時候)之外,看起來和Observable.just()
所做的沒什么兩樣。
現在唯一的問題是,自從閱讀Dávid Karnok的解讀操作符系列文章后(譯者注:簡直不能更優秀,一定要看),我一直小心翼翼的編寫著自定義的操作符(譯者注:原著的意思是指,自定義操作符內部處理方式,如上面代碼中的subscriber.onNext(value)
等)。通過閱讀該系列,我發現很難寫出正確的操作符。來看看這篇文章,Observable.just()
為了支持背壓(譯者注:例如Observable.zip()操作符)和退訂是如何做出改變的。
當然,上面那段代碼是能正確運行的,至少現在看來它是OK噠,但是隨著RxJava版本的不斷迭代,鬼知道以后能不能。而且我也不知道類似背壓和退訂等操作能否安全的向下兼容。更何況,我又不是操作符開發專家。所以,我試著避免自定義操作符,除非萬不得已。
簡單粗暴
這里有一種不需要自定義操作符的實現方式:
public Observable<String> valueObservable() {
return Observable.defer(new Func0<Observable<String>>() {
@Override public Observable<String> call() {
return Observable.just(value);
}
});
}
我所做的就是用defer()
操作符封裝原始代碼,但現在的表現正是我想要的。defer()
中的代碼直到被訂閱才會執行。我們只需要在請求數據的時候調用Observable.just()
就哦了。
我更喜歡這個解決方案的原因:
比
Observable.create()
更簡單,不再需要手動調用onCompleted()
。使用內置操作符,這種方式(可能)更得到官方的肯定。
使用defer()
操作符的唯一缺點就是,每次訂閱都會創建一個新的Observable
對象。create()
操作符則為每一個訂閱者都使用同一個函數,所以,后者效率更高。一如既往地,如果有必要可以親測性能或者嘗試優化。
深入
上面代碼僅僅是為講解所用,但是,切換到實際生產中,我們需要用BehaviorSubject
替換所有代碼。讓我們來看一些更復雜的東西。
假設需要一個方法,首先將數據寫進磁盤,然后再作為結果返回。這是一種用defer()
操作符的實現:
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);
}
});
}
這個例子稍微復雜一些,將數據寫進磁盤的同時如果拋出異常并捕獲,則立即調用onError
,基本的思路是相同的,那就是:在訂閱發生之前,不希望執行任何代碼。
其實,有很多方式可以解決上面的問題,雖然使用defer()
操作符只是其中之一,但是,使用起來真的很方便。