用一張圖解釋RxJava中的線程控制

otyj9021 8年前發布 | 15K 次閱讀 RxJava Java開發

上周五和團隊一起討論了RxJava的用法和實現機制。在討論中,@堅堅老師 問了一個有趣的問題:如果調用鏈中包含多個subscribeOn和observeOn,會是什么情況?

這實際上是一個至關重要的問題,因為在任何情況下,我們都應該弄清楚我們寫的每一行代碼到底是運行在哪個線程上。這個問題絕對不能含糊。

假設有下面這段偽代碼:

Observable.create(...)
    .lift1(...)
    .subscribeOn(scheduler1)
    .lift2(...)
    .observeOn(scheduler2)
    .lift3(...)
    .subscribeOn(scheduler3)
    .lift4(...)
    .observeOn(scheduler4)
    .doOnSubscribe(...)
    .subscribeOn(scheduler5)
    .observeOn(scheduler6)
    .subscribe(...);

那么在這段代碼中:

  • lift1, lift2, lift3, lift4指定的代碼分別在哪個線程執行?
  • doOnSubscribe指定的代碼在哪個線程執行?
  • 產生事件的代碼(create指定的代碼)在哪個線程執行?
  • 消費事件的代碼(subscribe指定的代碼)在哪個線程執行?

相信很多同學會覺得這段代碼多少有些令人暈眩,在實際中是不太可能出現的。確實,實際中的代碼大都沒有這么復雜,但弄清它有助于我們理解整個RxJava的實現流程。

用一張圖解釋RxJava中的線程控制

上面這幅圖表達了一個典型的RxJava調用鏈中控制流的傳遞過程。它可以分成兩個階段:

  1. 驅動階段。整個異步事件流的觸發由subscribe開始。它發起了一個反向驅動過程,跨過每一個中間的Observable和OnSubscribe,到達第一個Observable(產生事件的源頭)。對應圖中的(1)和(2)。
  2. 事件發射階段。第一個Observable開始產生事件,然后事件流就開始正向傳遞,經過每一個中間的Observable,最終到達Subscriber(事件的消費者)。對應圖中的(3)。

我們分析一下這整個流程,其中有幾點需要特別說明一下:

  • 圖中的(1)對應的是調用前一級Observable的OnSubscribe.call,是個無返回值的方法,因此可以切換線程,從而變為異步的。所以用虛線表示。
  • 圖中的(2)對應的是lift操作指定的Operator.call,是個有返回值的方法(輸入一個Subscriber,返回一個新的Subscriber)。因此,它只能同步調用,不能切換線程。所以用實線表示。
  • 圖中的(3)對應的是調用后一級Observable對應的Subscriber(onNext, onCompleted, onError),也都是無返回值的方法,因此可以切換線程,從而變為異步的。所以也用虛線表示。
  • observeOn是基于lift實現的,且切換線程的動作發生在Subscriber(onNext, onCompleted, onError),因此它影響(3)流程上在它后面的所有lift變換。
  • subscribeOn不是基于lift實現的,它直接在調用前一級Observable的OnSubscribe時切換線程。因此,它影響(1)流程上在它前面的所有OnSubscribe調用,直到產生事件的源頭;然后,(3)流程上的所有lift操作也會在新切換到的線程上,直到碰到一個observeOn操作。
  • doOnSubscribe稍微特殊一點。它雖然是基于lift實現的,但它所指定的代碼發生在Operator.call中,不像其它的lift操作(比如filter, map, reduce等),它們指定的代碼發生在Subscriber。因此它的執行線程受它后面的subscribeOn的影響。

根據上面的分析,就很容易得到下面的結論了:

  • 產生事件的代碼(create指定的代碼)和doOnSubscribe指定的代碼,在它們后面最近的一個subscribeOn指定的Scheduler上執行;如果它們后面沒有subscribeOn了,那么它們就在調用subscribe方法的那一個線程上執行。
  • 普通的lift操作(比如filter, map, reduce等)和消費事件的代碼(subscribe指定的代碼),在它們前面最近的一個observeOn指定的Scheduler上執行;如果它們前面沒有observeOn了,那么它們就在整個調用鏈的第一個subscribeOn指定的Scheduler上執行;如果沒找到subscribeOn調用,那么它們就在調用subscribe方法的那一個線程上執行。

把這些結論應用在本文開始的那段代碼上,我們很快能得到:

  • 產生事件的代碼(create指定的代碼)在scheduler1上執行;
  • lift1和lift2指定的代碼在scheduler1上執行;
  • lift3和lift4指定的代碼在scheduler2上執行;
  • doOnSubscribe指定的代碼在scheduler5上執行;
  • 消費事件的代碼(subscribe指定的代碼)在scheduler6上執行。


來源:http://zhangtielei.com/posts/blog-rxandroid-schedulers.html

 本文由用戶 otyj9021 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!