使用RxJava實現延遲訂閱

發表於2015-12-23

我越來越喜歡把RxJava的defer()操作符作為一個工具來使用,以確保Observable程式碼在被訂閱後才執行(而不是建立後立即執行)。我之前寫過一些有關defer()的程式碼,但是,現在我想做更詳細的描述。

假設,有個資料類:

這段程式碼在執行後會列印出什麼呢?

如果你認為會列印出“Some Value”,那就錯了。而實際列印結果是“null”。因為在呼叫Observable.just()的時候,value已經初始化了。

just()from()這類能夠建立Observable的操作符(譯者注:建立Observable的操作符)在建立之初,就已經儲存了物件的值,而不被訂閱的時候。這種情況,顯然不是預期表現,我想要的valueObservable()是無論什麼時候請求,都能夠表現為當前值。

自助

一個解決辦法就是使用Observable.create(),因為它允許為每個訂閱者精確控制事件的傳送。

現在,valueObservable()將在訂閱的時候傳送當前值(事件)。它除了在訂閱的時候才獲取value(而不是建立的時候)之外,看起來和Observable.just()所做的沒什麼兩樣。

現在唯一的問題是,自從閱讀Dávid Karnok的解讀操作符系列文章後(譯者注:簡直不能更優秀,一定要看),我一直小心翼翼的編寫著自定義的操作符(譯者注:原著的意思是指,自定義操作符內部處理方式,如上面程式碼中的subscriber.onNext(value)等)。通過閱讀該系列,我發現很難寫出正確的操作符。來看看這篇文章Observable.just()為了支援背壓(譯者注:例如Observable.zip()操作符)和退訂是如何做出改變的。

當然,上面那段程式碼是能正確執行的,至少現在看來它是OK噠,但是隨著RxJava版本的不斷迭代,鬼知道以後能不能。而且我也不知道類似背壓和退訂等操作能否安全的向下相容。更何況,我又不是操作符開發專家。所以,我試著避免自定義操作符,除非萬不得已。

簡單粗暴

這裡有一種不需要自定義操作符的實現方式:

我所做的就是用defer()操作符封裝原始程式碼,但現在的表現正是我想要的。defer()中的程式碼直到被訂閱才會執行。我們只需要在請求資料的時候呼叫Observable.just()就哦了。

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

  1. Observable.create()更簡單,不再需要手動呼叫onCompleted()
  2. 使用內建操作符,這種方式(可能)更得到官方的肯定。

使用defer()操作符的唯一缺點就是,每次訂閱都會建立一個新的Observable物件。create()操作符則為每一個訂閱者都使用同一個函式,所以,後者效率更高。一如既往地,如果有必要可以親測效能或者嘗試優化。

深入

上面程式碼僅僅是為講解所用,但是,切換到實際生產中,我們需要用BehaviorSubject替換所有程式碼。讓我們來看一些更復雜的東西。

假設需要一個方法,首先將資料寫進磁碟,然後再作為結果返回。這是一種用defer()操作符的實現:

這個例子稍微複雜一些,將資料寫進磁碟的同時如果丟擲異常並捕獲,則立即呼叫onError,基本的思路是相同的,那就是:在訂閱發生之前,不希望執行任何程式碼。

其實,有很多方式可以解決上面的問題,雖然使用defer()操作符只是其中之一,但是,使用起來真的很方便。

相關文章