前面在 EventBus设计与实现分析——特性介绍中介绍了EventBus的基本用法,及其提供的大多数特性的用法;在EventBus设计与实现分析——订阅者的注册 中介绍了EventBus中订阅者注册的过程。这里就继续分析EventBus的代码,来了解其事件发布的过程。
事件的发布
如我们前面已经了解到的,在EventBus中,有两种不同类型得事件,一种是普通事件,事件被通知给订阅者之后即被丢弃,另一种是Sticky事件,事件在被通知给订阅者之后会被保存起来,下次有订阅者注册针对这种事件的订阅时,订阅者会直接得到通知。
在EventBus中,会以两个不同的方法来发布这两种不同类型的事件,这两个方法分别是post(Object event)和postSticky(Object event):
postSticky()仅是在保存了事件之后调用post()来发布事件而已。而在post()中,会借助于PostingThreadState来执行事件发布的过程。PostingThreadState为发布的事件提供了排队功能,同时它还描述一些发布的线程状态。PostingThreadState还是发布过程跟外界交流的一个窗口,外部可通过EventBus类提供的一些方法来控制这个状态,进而影响发布过程,比如取消发布等操作。PostingThreadState对象在ThreadLocal变量中保存,可见发布的事件的队列是每个线程一个的。post()方法会逐个取出事件队列中的每一个事件,调用postSingleEvent()方法来发布。
postSingleEvent()要发布事件,首先需要找到订阅者,我们前面在 订阅者的注册 中看到,订阅者注册时会在subscriptionsByEventType中保存事件类型和订阅者的映射关系,那要找到订阅者岂不是很容易?
其实不完全是。关键是对于事件类型的处理。要通知的事件类型的订阅者不一定仅仅包含事件对象本身的类型的订阅者,还可能要通知事件类型的父类或实现的接口的类型的订阅者。在eventInheritance被置为true时,就需要通知事件类型的父类或实现的接口的类型的订阅者。lookupAllEventTypes()和addInterfaces()就用于查找所有这样的类型。
postSingleEvent()会逐个事件类型的去通知相应得订阅者,这一任务由postSingleEventForEventType()来完成。而在postSingleEventForEventType()中则是根据subscriptionsByEventType找到所有的订阅者方法,并通过postToSubscription方法来逐个的向这些订阅者方法通知事件。
在postToSubscription()中事件的通知又分为同步的通知和异步的通知。同步的通知是直接调用invokeSubscriber(Subscription subscription, Object event)方法,这会将事件对象传递给订阅者方法进行调用。而异步的通知则是将事件及订阅者抛给某个poster就结束。
对于某个订阅者的通知要采用同步通知还是异步通知则需要根据订阅者的ThreadMode及事件发布的线程来定。具体得规则为:
订阅者的线程模式是POSTING ——————————–> 同步通知
订阅者的线程模式是MAIN + 事件发布线程是主线程 —————> 同步通知
订阅者的线程模式是BACKGROUND + 事件发布线程不是主线程 ——> 同步通知
订阅者的线程模式是BACKGROUND + 事件发布线程是主线程 ——–> 异步通知
订阅者的线程模式是MAIN + 事件发布线程不是主线程 ————–> 异步通知
订阅者的线程模式是ASYNC ———————————-> 异步通知
同步通知和异步通知各三种。但三种异步通知本身又各不相同,它们分别由三种不同的Poster来处理,订阅者的线程模式是BACKGROUND
+ 事件发布线程是主线程的异步通知由BackgroundPoster
来处理,订阅者的线程模式是MAIN
+ 事件发布线程不是主线程的异步通知由HandlerPoster
来处理,而订阅者的线程模式是ASYNC
的异步通知由AsyncPoster
来处理。
接着就来看一下这些Poster。首先是HandlerPoster:
这是一个Handler。其内部有一个PendingPostQueue queue,enqueue()操作即是用描述订阅者方法的Subscription对象和事件对象构造一个PendingPost对象,然后将这个PendingPost对象放入queue中,并在Handler没有在处理事件分发时发送一个消息来唤醒对于事件分发的处理。
而在handleMessage()中,则是逐个从queue中取出PendingPost对象,并通过EventBus的invokeSubscriber(PendingPost pendingPost)来传递事件对象调用订阅者方法。这里调用的invokeSubscriber()方法与前面那个同步版本略有差异,它会将Subscription对象和事件对象从PendingPost对象中提取出来,并调用同步版的方法,同时还会释放PendingPost对象。
这里有一个蛮巧妙得设计,就是那个maxMillisInsideHandleMessage,它用于限制一次事件发布所能消耗的最多的主线程时间。如果事件限制到了的时候订阅者没有通知完,则会发送一个消息,在下一轮中继续处理。
这是一个典型的生产者-消费者模型,生产者是事件的发布者线程,而消费者则是主线程。
PendingPost对象是通过一个链表来组织的。
还有PendingPost:
PendingPostQueue是一个线程安全的链表,其中链表的节点是PendingPost,它提供了最最基本的入队和出队操作而已。PendingPost再次用了对象池,它提供了获取对象和释放对象的方法。EventBus的作者真的还是蛮喜欢用对象池的嘛。
然后再来看BackgroundPoster:
BackgroundPoster与HandlerPoster还是挺像的。两者的差别在于BackgroundPoster是一个Runnable,它的enqueue()操作唤醒对于事件分发的处理的方法,是将对象本身放进EventBus的ExecutorService中执行来实现的;另外在处理事件分发的run()方法中,无需像HandlerPoster的handleMessage()方法那样考虑时间限制,它会一次性的将队列中所有的PendingPost处理完才结束。
对于某一个特定事件,一次性的将所有的PendingPost递交给BackgroundPoster,因而大概率的它们会在同一个线程被通知。但如果订阅者对事件的处理过快,在下一个PendingPost还没来得及入队时即执行结束,则还是有可能在不同的线程中被通知。
最后再来看一下AsyncPoster:
它会对每一个通知(订阅者方法 + 订阅者对象 + 事件对象)都起一个不同的task来进行。
用一张图来总结EventBus中事件通知的过程:
EventBus发布事件的过程大体如此。