在实际环境中,你会发现RxBus还是有一些问题的。
你需要RxBus支持Sticky功能。
你会发现在你订阅了某个事件后,在后续接收到该事件时,处理的过程中发生了异常,你可能会发现后续的事件都接收不到了!
我将分2篇文章分别给出其方案,这篇介绍如何实现Sticky,另外一篇介绍RxBus中的异常处理方案:
什么是Sticky事件?
在Android开发中,Sticky事件只指事件消费者在事件发布之后才注册的也能接收到该事件的特殊类型。Android中就有这样的实例,也就是Sticky Broadcast,即粘性广播。正常情况下如果发送者发送了某个广播,而接收者在这个广播发送后才注册自己的Receiver,这时接收者便无法接收到刚才的广播,为此Android引入了StickyBroadcast,在广播发送结束后会保存刚刚发送的广播(Intent),这样当接收者注册完Receiver后就可以接收到刚才已经发布的广播。这就使得我们可以预先处理一些事件,让有消费者时再把这些事件投递给消费者。
Subject
我们在实现简单的RxBus时使用了PublishSubject,其实RxJava提供给开发者4种Subject:
PublishSubject,BehaviorSubject ,BehaviorSubject,AsyncSubject。
PublishSubject只会给在订阅者订阅的时间点之后的数据发送给观察者。
BehaviorSubject在订阅者订阅时,会发送其最近发送的数据(如果此时还没有收到任何数据,它会发送一个默认值)。
ReplaySubject在订阅者订阅时,会发送所有的数据给订阅者,无论它们是何时订阅的。
AsyncSubject只在原Observable事件序列完成后,发送最后一个数据,后续如果还有订阅者继续订阅该Subject, 则可以直接接收到最后一个值。
从上图来看,似乎BehaviorSubject和ReplaySubject具备Sticky的特性。
BehaviorSubject方案
BehaviorSubject似乎完全符合Sticky的定义,但是你发现了它只能保存最近的那个事件。
有这样的场景:如果订阅者A订阅了Event1,订阅者B订阅了Event2,而此时BehaviorSubject事件队列里是[…, Event2, Event1],当订阅者订阅时,因为保存的是最近的事件:即Event1,所以订阅者B是接收不到Event2的。
解决办法就是:
每个Event类型都各自创建一个对应的BehaviorSubject,这样的话资源开销比较大,并且该Sticky事件总线和普通的RxBus事件总线不能共享,即:普通事件和Sticky事件是独立的,因为普通事件是基于PublishSubject的, 暂时放弃该方案!
ReplaySubject方案
ReplaySubject可以保存发送过的所有数据事件。
因为保存了所有的数据事件,所以不管什么类型的Event,我们只要过滤该类型,并让其发送最近的那个Event即可满足Sticky事件了。但是获取最近的对应事件是个难点,因为最符合需求的操作符takeLast()仅在订阅事件结束时(即:onCompleted())才会发送最后的那个数据事件,而我们的RxBus正常情况下应该是尽量避免其订阅事件结束的。(我没能找到合适的操作符,如果你知道,请告知我)
所以BehaviorSubject也是比较难实现Sticky特性的。