前言
EventBus 是一款针对 Android 优化的发布-订阅事件总线。它简化了应用程序内各组件间的通信,开销小,代码更简洁,以及将发送者和接受者解耦。
本文将从源码的角度分析 EventBus 的实现原理,分析版本为 EventBus-3.0.0。
EventBus 的使用
在分析 EventBus 的源码前,先简单回顾一下 EventBus的使用。它的使用分为以下几个步骤:
定义一个事件类(实体类)
1
2
3public class MessageEvent {
...
}事件订阅者进行事件注册
1
EventBus.getDefault().register(subscriber);
事件订阅者处理事件
1
2
3
4(threadMode = ThreadMode.MAIN)
public void onMessageEventCome(MessageEvent event) {
// 处理事件
}事件发布者发送事件
1
EventBus.getDefault().post(event);
事件订阅者取消事件订阅
1
EventBus.getDefault().unregister(subscriber);
下面将针对这几个步骤进行源码分析
源码分析
获得 EventBus 实例
在进行订阅和发送事件的时候,都需要先通过 EventBus.getDefault() 获得 EventBus 实例。
现在看一下 EventBus 的 getDefault 方法:1
2
3
4
5
6
7
8
9
10
11
12static volatile EventBus defaultInstance;
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
可以看到,EventBus 是一个单例,采用了 DCL 方式实现。
再看下 EventBus 的构造方法:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
public EventBus() {
this(DEFAULT_BUILDER);
}
EventBus(EventBusBuilder builder) {
subscriptionsByEventType = new HashMap<>();
typesBySubscriber = new HashMap<>();
stickyEvents = new ConcurrentHashMap<>();
mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);
backgroundPoster = new BackgroundPoster(this);
asyncPoster = new AsyncPoster(this);
indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
builder.strictMethodVerification, builder.ignoreGeneratedIndex);
logSubscriberExceptions = builder.logSubscriberExceptions;
logNoSubscriberMessages = builder.logNoSubscriberMessages;
sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
throwSubscriberException = builder.throwSubscriberException;
eventInheritance = builder.eventInheritance;
executorService = builder.executorService;
}
可以看到,在初始化时采用了 Builder 模式
订阅者进行注册
事件订阅者要想接收到事件,必须先进行注册:1
EventBus.getDefault().register(subscriber);
看下 register 方法的实现:
EventBus#register
1 | public void register(Object subscriber) { |
在该方法中,首先获取到订阅者的 Class 对象,然后根据 Class 对象获得订阅者的所有订阅方法,最后订阅这些方法。
先看一下 SubscriberMethodFinder 的 findSubscriberMethods 方法:
SubscriberMethodFinder#findSubscriberMethods
1 | List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) { |
该方法首先查看是否有缓存,如果有缓存就从缓存中获取并返回。没有缓存的话,根据情况,从反射或从注解器生成的 MyEventBusIndex 中获得订阅者的订阅方法信息。最后如果获取成功就添加进缓存并返回,否则抛出异常。
下面分别看下两种获取订阅方法信息的方式,首先看反射的方式,这种方式调用 findUsingReflection 方法:
SubscriberMethodFinder#findUsingReflection
1 | private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) { |
该方法创建了一个 FindState 对象,用于辅助获取订阅方法,重点看 findUsingReflectionInSingleClass 方法,该方法使用反射方式获取单个类的订阅方法。
SubscriberMethodFinder#findUsingReflectionInSingleClass
1 | private void findUsingReflectionInSingleClass(FindState findState) { |
该方法先通过反射获得订阅者的所有方法,再对这些方法进行筛选,找出订阅方法。
筛选规则为:
- 忽略非 public 方法和 static、abstract等修饰的方法
- 订阅方法只能有一个参数
- 订阅方法有 Subscribe 注解
- 子类继承并重写了父类的订阅方法,那么只会添加子类的方法,父类的方法会忽略
看完反射方式获取订阅方法,下面看一下另一种方式,这种方式从注解器生成的 MyEventBusIndex 中获取,调用的是 findUsingInfo 方法:
SubscriberMethodFinder#findUsingInfo
1 | private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) { |
该方法也是先获得 FindState 对象,然后通过在 EventBusBuilder 配置的 MyEventBusIndex 获取信息,如果获取失败就利用反射方式获取。
所以在默认情况下,如果我们没有专门配置 MyEventBusIndex,那么都是通过反射方式来获取订阅方法的。
获取订阅方法就分析到这里,下面回到 register:1
2
3
4
5
6
7
8// 获得订阅者的所有订阅方法
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
// 订阅所有方法
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
在获取到订阅方法后,会遍历各方法,调用 subscribe 方法进行订阅。下面看一下 subscribe 方法:
EventBus#subscribe
1 | private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) { |
subscribe 方法主要做了这几件事:
- 获得事件类型,并根据订阅者和订阅方法创建一个 Subscription 对象
- 根据优先级将 Subscription 添加到当前事件类型对应的 Subscription 集合。注意不能重复添加,否则会抛出异常
- 将当前事件类型添加到订阅者的事件类型集合
- 对 sticky 事件的处理:从 sticky 事件保存队列中取出该事件类型的事件并发送给当前订阅者
小结
订阅者进行注册的步骤如下:
- 根据订阅者的 Class 对象获得订阅者的所有订阅方法。获取的时候先从缓存中获取,没有的话就通过反射或从注解器生成的 MyEventBusIndex 中获取,默认情况下先从 MyEventBusIndex 中获取,获取不到再采取反射方式,先通过反射获取到所有方法,然后再根据修饰符、参数个数、注解筛选出订阅方法。
- 对每个订阅方法进行订阅,订阅步骤如下:
- 获得事件类型,并根据订阅者和订阅方法创建一个 Subscription 对象
- 根据优先级将 Subscription 添加到当前事件类型对应的 Subscription 集合
- 将当前事件类型添加到订阅者的事件类型集合
- 对 sticky 事件的处理:从 sticky 事件保存队列中取出对应事件并发送给订阅者
发布事件
事件发布者通过 post 或 postSticky 方法发布事件,例如:1
EventBus.getDefault().post(event);
先看 post 方法:
EventBus#post
1 | public void post(Object event) { |
EventBus 用 ThreadLocal 存储每个线程的 PostingThreadState。post 事件时,会将事件添加到当前线程的事件队列中,先等待前面的事件处理完毕,接着将事件交给 postSingleEvent 处理:
EventBus#postSingleEvent
1 | private void postSingleEvent(Object event, PostingThreadState postingState) throws Error { |
根据 eventInheritance 属性判断是否还要找出发布事件的所有父类。最后继续调用 postSingleEventForEventType 方法发布事件:
EventBus#postSingleEventForEventType
1 | private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) { |
先得到事件类型对应的 Subscription 对象集合,遍历该集合,发布事件到具体的订阅者。发布过程调用 postToSubscription 方法:
EventBus#postToSubscription
1 | private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) { |
根据订阅方法的 threadMode 在相应的线程进行事件处理。进行事件处理调用的是 invokeSubscriber 方法:
EventBus#invokeSubscriber
1 | void invokeSubscriber(Subscription subscription, Object event) { |
最终,在 invokeSubscriber 方法中调用了订阅者的订阅方法并将事件作为参数传递过去。
小结
发布事件的步骤如下:
- post 事件后,先将事件添加到当前线程的事件队列中,等待前面的事件处理完毕。
- 根据 eventInheritance 属性判断是否还要将事件发送给带有事件父类的订阅方法。
- 得到事件类型,根据事件类型得到所有的订阅者和订阅方法信息。
- 根据订阅方法的 threadMode,在相应的线程里,利用反射的方式调用订阅者的订阅方法并将事件作为参数传递过去。
订阅者取消注册
取消注册调用的是 unregister 方法:
EventBus#unregister
1 | public synchronized void unregister(Object subscriber) { |
总的来说,取消注册过程就是把注册过程保存起来的当前订阅者和订阅方法信息都移除。
设计模式
观察者模式
观察者模式:定义了对象间的一对多依赖关系,每当一个对象改变状态时,它的所有依赖者都会得到通知并自动更新。
对于 EventBus 来说,事件类是被观察者,订阅者类是观察者。当事件出现的时候,会通知观察者,使得观察者的订阅方法能够自动调用。
EventBus 又和一般的观察者模式不太一样,它并不需要事件类保存观察者名单和通知观察者,因为这样对于每一个事件类都要实现相同的操作,就太过繁琐和低效率了。因此,EventBus 就充当了被观察者和观察者的桥梁,把事件的很多责任都抽离出来,事件本身只需要存储数据,别的交给 EventBus 就行了。