0. 基础
EventBus 是一个基于事件的“发布-订阅”模式的事件总线框架,有以下基础的概念:
- 订阅者(Subscriber):订阅消息的角色;
- 发布者(Publisher):发布事件的角色;
- 事件(Event):代表一个可以被发布的消息;
- 事件总线(EventBus):用于管理订阅关系和消息的发布的总线;
引用 EventBus官网上关于“发布-订阅” 模式的经典示意图:
从上图中可以看出,整个模式是围着着“事件”的传递进行的,一个事件从发布者
,经由总线
调度后,被分发给多个订阅者
,然后完成整个过程。这个也被称为“生产者-消费者” 模式。
可见该模式的步骤如下:
- 预先定义一些事件;
订阅者
去总线
那里订阅一些自己关注的事件
;- 当某些条件满足后,
发布者
通过总线
发布事件
; - 订阅者收到事件后进行各自相应的处理;
- 如果订阅者不再对事件感兴趣的话,就去总线处取消订阅,后续的事件也将不会再通知到这个订阅者。
1. 源码分析
“订阅-发布” 模式中各个角色的实现:
角色 | 实现 | 说明 |
---|---|---|
订阅者(Subscriber) | SubscriberMethod | 订阅者是一个个普通的类,实际上是以方法为单位作为真正的订阅者的。SubscriberMethod订阅方法,代表在EventBus中注册的一个方法 |
发布者(Publisher) | EventBus | 在EventBus的框架中,是由EventBus这个类来“兼任“了消息发布者的角色 |
事件(Event) | 一些预先定义的POJO格式的java类,也是普通的Java类 | 在EventBus中是用一个一个的类来描述事件的,根据类的Class对象类区分 |
事件总线(EventBus) | EventBus类 | 主要的API类,包含有总线的职责 |
其他一些关键类:
- Subscription:订阅信息,内部包含有订阅者和订阅方法的信息;
- EventBusBuilder:用于构造一个EventBus 对象的Builder类;
- SubscriberMethodFinder:用于在类中通过反射来查找某个注解的方法;
EventBus类是整个框架的接口调用类,是使用Builder 设计模式
来进行对象的构造的,通过EventBusBuilder
类来传入各种参数。各个参数参考官方的说明:EventBus配置
1.1 订阅过程
EventBus框架是使用Java注解语法来实现的,这样做是为了使用的简单,只需要在订阅者类的内部给一些方法加上注解,如下:
在注解添加之后,需要给该方法(订阅者)进行注册,在注册之后,一个订阅者的订阅过程就完成了。事件的订阅过程就是从EventBus.register(Object subscriber)
订阅方法开始的。register 这个方法的内容很简单:
|
|
那么,继续跟踪subscribe(Object subscriber, SubscriberMethod subscriberMethod)
,能够看到最终的订阅过程,是把一个新的订阅放在类EventBus 的 subscriptionsByEventType
成员中了,该成员是一个Map对象,里面存放的是EventType作为Key,List
|
|
以上就是订阅过程的源码,总的来说,分为以下几个步骤(在调用 EventBus.register 之后):
- 通过反射查找注册的订阅者对象中的订阅方法(SubscriberMethod)的列表;
- 把订阅方法列表中的
SubscriberMethod
对象逐个调用subscribe
方法;
2.1. 在subscribe
方法中,先通过 SubscriberMethod 对象构造出一个Subscription
对象来;
2.2. 在subscribe
方法中,把构造出的Subscription
对象依次正确地放在EventBus 的subscriptionsByEventType
和subscribedEvents
两个成员中,这两个成员都是Map类型的集合,存放的是已订阅的订阅信息;
2.3. 在subscribe
方法中,如果注册的是粘性事件,处理粘性事件的特殊逻辑;
总的来说,就是收集订阅信息,把他们放在EventBus 的subscriptionsByEventType
和 subscribedEvents
两个成员中。
以上,就完成了订阅的流程。
1.2 取消订阅过程
取消发布的流程,是通过调用EventBus 的public synchronized void unregister(Object subscriber)
方法来完成的。和订阅的原理类似,即是通过操作typesBySubscriber
变量的内容来完成的,不同的是订阅过程是往集合类里面添加数据,而取消注册正好相反,是从集合中移除数据。代码如下:
|
|
从上面的源码中可以看出,取消订阅的过程是和订阅的正好相反的,订阅的过程是把订阅信息从EventBus 的subscriptionsByEventType
和 subscribedEvents
两个成员中移除。
1.3 发布事件过程
事件的发布过程,是通过 EventBus.post(Object event)
方法来发布一个事件,post方法的源码如下:
首先,我们能够看到,事件的发布,是由一个队列控制着的,保证了事件的发生顺序是按照post
方法的调用顺序进行发布的。EventBus 中的currentPostingThreadState 是一个ThreadLocal对象,里面包含有一个 PostingThreadState 类型的对象,这个PostingThreadState 是EventBus 的静态内部类,代表着当前线程正在发布事件的状态,内部仅仅定义了一些成员,没有逻辑在内部,可以理解为一个数据的集合:
每一个被post 的事件,都会被放在当前线程的PostingThreadState 对象的 eventQueue 里面,这个 eventQueue 是一个列表(当作队列使用),在post 方法里面循环遍历 eventQueue,依次调用postSingleEvent
方法,它的源码如下:
在postSingleEvent 方法的内部,会处理 eventInheritance
参数的逻辑,分别针对 eventInheritance设为true 和false两种情况进行了不同的处理,但是相同的是,最终都会调用到 postSingleEventForEventType
方法,它的逻辑如下
接下来,是到了 postToSubscription 方法中:
从上面的分析,可以看到发布事件的调用顺序是:
调用的步骤是:
- 调用 post() 方法开始发布事件;
- 把需要发布的事件,放在当前线程的发布队列里面;
- 循环把发布队列中的事件挨个进行处理;
- 处理 eventInheritance 参数的逻辑;
- 按照需要发布事件的线程类型(ThreadMode),来确定是直接调用invokeSubscriber方法来发布,还是先加到目标线程的队列中;
- 最终,调用 invokeSubscriber 方法,执行订阅方法(Method) 的invoke方法,通过反射来执行订阅方法,完成事件的发布;
以上,就是普通事件的发布过程。
1.4 发布粘性事件(Sticky Event)
粘性事件,是相对于普通事件来说的,对于普通的事件来说:先订阅、再发布事件是一个标准的步骤,也就是说,如果想要能接收到一个事件的话,那前提是得先订阅了这个事件,如果说在事件发布的时候,还没订阅这个事件,那么这个订阅者是收不到的这个事件的。
然而,在某些特殊的场景中,是能够希望订阅者的订阅过程哪怕是晚一点也能够收到事件的,能够实现的是,只要一个订阅者订阅了事件,那哪怕是在订阅前发生的这个事件,这个订阅者也照样能收得到。对于这样的特殊的事件,被称为粘性事件(Sticky Event)
。粘性这个概念,指的就是这样的场景,这个概念在Android系统中也有,就是粘性广播
,和这里的粘性事件类似的含义。
粘性事件的发布流程,大体上和普通事件的一样,是通过postSticky 方法实现的:
从源码中可以看出,Sticky Event 的发布,其实就是先把Sticky Event加入到stickyEvents
集合里面,然后又调用了 post 方法。也就是说比普通的事件的发布多了一个加入到 stickyEvents 的过程。
这个 stickyEvents
是EventBus 的一个成员,是一个Map类型的集合对象,这个对象在 1.3 发布事件过程 里面已经提到过,在订阅的时候,当订阅的事件是Sticky 的时候,会check当前的 stickyEvents 里面是否存在该类型的Event,有的话,就直接调用到 postToSubscription
执行事件的发布了。
由此就可以明白,所谓的Sticky Event,其实就是在postSticky之后,把事件在 stickyEvents 成员中缓存起来,当后续有订阅者来订阅该类型的Event 的时候,则直接从 stickyEvents 缓存中读取事件并直接发布事件。这样,就做到了“先发布、后订阅”也能让订阅者收到粘性事件
了。
1.5 线程调度
EventBus中的事件发布,支持多线程的调度,多线程的调度主要是用在事件的发布过程中的postToSubscription
方法中:
从源码中可以看出来,线程的调度,都是通过Poster
接口的enqueue
方法来实现的。Poster接口代表一个和线程关联的事件队列,包含一个像线程事件队列的接口enqueue()
方法。
Poster 接口的定义很简答,只有一个装载事件到队列的方法:
Poster有多个子类的实现,分别对应了不同的线程处理模式:
- AsyncPoster: 对应于
ThreadMode.ASYNC
模式,该模式总是把事件在一个新的线程中执行。内部是使用了EventBus 的 executorService 成员(线程池对象)来执行的; - BackgroundPoster: 对应于
ThreadMode.BACKGROUND
模式,该模式会把事件发布到后台线程中执行。内部也是使用了EventBus 的 executorService 成员(线程池对象)来执行的; - HandlerPoster: 对应于
ThreadMode.MAIN
模式,该模式是把事件发布到Android的主线程中执行。HandlerPoster 是继承了Android 的Handler 类。
在3个Poster 的子类中,AsynPoster 和BackgroundPoster的原理是类似的,都是通过EventBus的线程池对象来执行的。以BackgroundPoster 为例,它的 enqueue 方法如下:
而HandlerPoster类,它的构造,是使用了一个外部的 Looper对象,关于Looper机制,是Android平台上面的Event-Loop 机制的实现,这里不再赘述(这个外部的Looper对象,针对Android平台上来说,默认就是Android的主线程的Looper):
调用上面这个构造方法的地方,是在 MainThreadSupport.createPoster() 里面,而里面的Looper参数,则来自EventBus 的mainThreadSupport
成员:
HandlerPoster 也实现了 Poster 的enqueue 方法:
从上面的源码中,能看出来对于ThreadMode.MAIN 模式的线程调度的实现,就是通过主线程的Looper关联一个Handler来实现消息的发布的。
以上,就是EventBus 的主要的源码的分析。