【譯】使用RxJava實現延遲訂閱

我越來越喜歡把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()就哦了。

我更喜歡這個解決方案的原因:

  1. Observable.create()更簡單,不再需要手動調用onCompleted()

  2. 使用內置操作符,這種方式(可能)更得到官方的肯定。

使用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()操作符只是其中之一,但是,使用起來真的很方便。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容