Rxjava中, subscribeOn及oberveOn方法切换线程发生的位置为什么设计为不同的?

最近在学习Rxjava的原理这一块,在切换线程这里产生了一个疑问,那就是为什么subscribeOn及oberveOn方法切换线程发生的位置是不同的?…
关注者
68
被浏览
5,385

3 个回答

最近刚开始研究RxJava,正好也在思考线程控制方面的问题。下面说一下我的理解。

正像StackOverflow上那段描述的,整个Observable数据流工作起来是分为两个阶段(或者说是两个lifecycle):upstream的subscription-time和downstream的runtime。

subscription-time的阶段,是为了发起和驱动数据流的启动,在内部实现上体现为OnSubscribe向上游的逐级调用(控制流向上游传递)。支持backpressure的producer request也属于这个阶段。除了producer request的情况之外,subscription-time阶段一般就是从下游到上游调用一次就结束了,最终到达生产者(以最上游的那个OnSubscribe来体现)。接下来数据流就开始向下游流动了。

而downstream的runtime阶段,就是数据本身向下游的流动,体现为onNext, onError, onComplete的调用。与subscription-time不同,数据项从上游往下游传递,不是一次就完了,而是多个数据项组成的数据流。

通过observeOn来切换runtime阶段的执行线程,是比较好理解的,情况也比较多。受它影响的observation side-effect就是通常所说的各种lift变换操作,比如filter, map, reduce等等。而通过subscribeOn切换线程能影响到的subscription-time阶段的side-effect,比较少见,但也有一些例子,比如:

  • doOnSubscribe,监听订阅发生的事件,可以用于在数据流动之前做一些准备工作。
  • doOnRequest,监听producer的request事件。

需要注意的是,生产者的执行线程只受subscribeOn控制,不受observeOn影响。subscribeOn指定的线程环境能一直维持到第一次observeOn出现之前,这样它就也能影响到各种lift变换操作。所以我们在网上会看到很多典型的代码中出现的lift操作线程都是由subscribeOn指定的,而observeOn只用来指定消费者线程。这也是StackOverflow上那段描述中“sometimes the net outcome is the same”的含义。

下面附上我昨天画的一张流程图:

图中左边(1)就是upstream方向,右边(3)就是downstream方向。

更多结合了RxJava实现的详细描述,欢迎阅读我昨天刚写的一篇短文:

用一张图解释RxJava中的线程控制

希望以上对题主有所帮助。

subscribeOn : 影响的是订阅流程,由下至上

observeOn: 影响的事件发射消费流程,由上到下。


"为这样设计的话subscribeOn方法只能生效一次"

并不是只能生效一次,而是如 大头鬼 所说,一般订阅流程(即生产者生产数据)不需要切换线程。

你试下多次的doOnSubscribe()配合多次的subscribeOn,即可看到线程切换的生效了。




首赞张铁蕾的文章很棒,学习了。

用一张图解释RxJava中的线程控制 - 铁蕾的个人博客

为什么?