Android 浅析 EventBus (二) 原理
前言
Linus Benedict Torvalds : RTFSC – Read The Fucking Source Code
概括
本次分析从两个方向深入,一个是从注册开始,一个是从发送消息开始。从这两个方向就能大致了解eventbus的运作原理。
注册原理
MainActivity
EventBus.getDefault().register(this);
EventBus的注册就从这里开始。
Step 1.EventBus.getDefault()
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
这是一句很典型的单例写法,整个eventbus在项目中是以单例的形式出现的。在初始化这块调用的是EventBusBuilder的默认参数。这也是Builder模式比较常用的。
Step 2.EventBus.register(Object subscriber)
这里是注册订阅者的地方,同样的注册方式有这么几个:
- register(Object subscriber)
- register(Object subscriber, int priority)
- registerSticky(Object subscriber)
- registerSticky(Object subscriber, int priority)
它们之间最主要的区别就是参数的不同。在实现上它们都是调用void register(Object subscriber, boolean sticky, int priority)
函数实现的。
private synchronized void register(Object subscriber, boolean sticky, int priority) {
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriber.getClass());
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod, sticky, priority);
}
}
这里首先调用findSubscriberMethods()
方法根据当前订阅者的类名查找到该类的所有订阅者函数。
在获取完所有订阅者函数后调用subscribe方法。
Step 3.EventBus.subscribe(subscriber, subscriberMethod, ...)
这里是注册函数的核心,分成三个部分:
- 通过
subscriptionsByEventType
得到这个Event类型所有订阅者信息队列,然后根据优先级将当前订阅者信息插入到队列里面。 - 在
typesBySubscriber
中得到当前订阅者订阅的所有事件队列,将此事件保存到队列中,用于后续取消订阅。 - 检查这个事件是否是 Sticky 事件,如果是则从stickyEvents事件保存队列中取出该事件类型最后一个事件发送给当前订阅者。
Step 4.EventBus.unregister(Object subscriber)
最后就是反注册,这里就比较简单了。
首先从typesBySubscriber
获取当前订阅者,然后找到此订阅者的所有类型,将此订阅者的所有类型从subscriptionsByEventType
表里删除。接着再把此订阅者从typesBySubscriber
中删除。
发送原理
MainActivity
public class MessageEvent {
public final String message;
public MessageEvent(String message) {
this.message = message;
}
}
public void onEventMainThread(MessageEvent event) {
}
EventBus.getDefault().post(new MessageEvent("hello eventbus"));
EventBus.getDefault().post(this);
EventBus的注册就从这里开始。其实这个就是一个序列化和反序列化的过程。将一段event打包然后再接受函数解包。
EventBus.post(Object event)
这个函数将收到的event发送到event bus。
首先将此事件保存到currentPostingThreadState
的事件队列里。
然后查看当前是否有事件在发送,然后调用postSingleEvent()
函数发送。
EventBus.postSingleEvent()
首先调用lookupAllEventTypes()
获取所有事件的类型,然后循环调用postSingleEventForEventType()
函数发送事件。
EventBus.lookupAllEventTypes(...)
这里从当前事件中获取父类和接口,一直往上循环获取直到最后。然后保存到eventTypesCache
变量里。
EventBus.postSingleEventForEventType(...)
这里就是获取每个事件,然后通过循环发送这些事件,会将事件的参数添加到PostingThreadState
结构体里传到postToSubscription()
函数来发送,这里就能区分是主界面线程还是非界面线程。最后再把参数都反初始化。
EventBus.postToSubscription(Subscription subscription, ...)
这个函数就是主要的分发函数,根据每个事件的threadMode来分发到各自相应的回调函数。
switch (subscription.subscriberMethod.threadMode) {
case PostThread:
case MainThread:
case BackgroundThread:
case Async:
}
这里主要就是分发到四个不同函数。
- invokeSubscriber()
- mainThreadPoster.enqueue()
- backgroundPoster.enqueue()
- asyncPoster.enqueue()
这里我们分别看下:
- PostThread:默认的 ThreadMode,表示在执行 Post 操作的线程直接调用订阅者的事件响应方法,不论该线程是否为主线程(UI 线程)。当该线程为主线程时,响应方法中不能有耗时操作,否则有卡主线程的风险。适用场景:对于是否在主线程执行无要求,但若Post线程为主线程,不能耗时的操作;
- MainThread:在主线程中执行响应方法。如果发布线程就是主线程,则直接调用订阅者的事件响应方法,否则通过主线程的 Handler 发送消息在主线程中处理——调用订阅者的事件响应函数。显然,MainThread类的方法也不能有耗时操作,以避免卡主线程。适用场景:必须在主线程执行的操作;
- BackgroundThread:在后台线程中执行响应方法。如果发布线程不是主线程,则直接调用订阅者的事件响应函数,否则启动唯一的后台线程去处理。由于后台线程是唯一的,当事件超过一个的时候,它们会被放在队列中依次执行,因此该类响应方法虽然没有PostThread类和MainThread类方法对性能敏感,但最好不要有重度耗时的操作或太频繁的轻度耗时操作,以造成其他操作等待。适用场景:操作轻微耗时且不会过于频繁,即一般的耗时操作都可以放在这里;
- Async:不论发布线程是否为主线程,都使用一个空闲线程来处理。和BackgroundThread不同的是,Async类的所有线程是相互独立的,因此不会出现卡线程的问题。适用场景:长耗时操作,例如网络访问。
这里我们可以看到从最开始eventbus就通过反射将需要调用的函数加载到eventbus的类里保存下来了。不过这里也可以知道其实eventbus也只是通过handler来调用主界面的线程。秘密揭开了,自己也可以尝试写一套。