Android 浅析 EventBus (二) 原理

前言

Linus Benedict Torvalds : RTFSC – Read The Funning Source Code

概括

本次分析从两个方向深入,一个是从注册开始,一个是从发送消息开始。从这两个方向就能大致了解eventbus的运作原理。

注册原理

register

MainActivity

EventBus.getDefault().register(this);
EventBus的注册就从这里开始。

Step 1.EventBus.getDefault()

1
2
3
4
5
6
7
8
9
10
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)

这里是注册订阅者的地方,同样的注册方式有这么几个:

  1. register(Object subscriber)
  2. register(Object subscriber, int priority)
  3. registerSticky(Object subscriber)
  4. registerSticky(Object subscriber, int priority)
    它们之间最主要的区别就是参数的不同。在实现上它们都是调用void register(Object subscriber, boolean sticky, int priority)函数实现的。
    1
    2
    3
    4
    5
    6
    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, …)

这里是注册函数的核心,分成三个部分:

  1. 通过subscriptionsByEventType得到这个Event类型所有订阅者信息队列,然后根据优先级将当前订阅者信息插入到队列里面。
  2. typesBySubscriber中得到当前订阅者订阅的所有事件队列,将此事件保存到队列中,用于后续取消订阅。
  3. 检查这个事件是否是 Sticky 事件,如果是则从stickyEvents事件保存队列中取出该事件类型最后一个事件发送给当前订阅者。

Step 4.EventBus.unregister(Object subscriber)

最后就是反注册,这里就比较简单了。
首先从typesBySubscriber获取当前订阅者,然后找到此订阅者的所有类型,将此订阅者的所有类型从subscriptionsByEventType表里删除。接着再把此订阅者从typesBySubscriber中删除。

发送原理

post

MainActivity

1
2
3
4
5
6
7
8
9
10
11
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来分发到各自相应的回调函数。

1
2
3
4
5
6
switch (subscription.subscriberMethod.threadMode) {
case PostThread:
case MainThread:
case BackgroundThread:
case Async:
}

这里主要就是分发到四个不同函数。

  1. invokeSubscriber()
  2. mainThreadPoster.enqueue()
  3. backgroundPoster.enqueue()
  4. asyncPoster.enqueue()

这里我们分别看下:

  1. PostThread:默认的 ThreadMode,表示在执行 Post 操作的线程直接调用订阅者的事件响应方法,不论该线程是否为主线程(UI 线程)。当该线程为主线程时,响应方法中不能有耗时操作,否则有卡主线程的风险。适用场景:对于是否在主线程执行无要求,但若Post线程为主线程,不能耗时的操作;
  2. MainThread:在主线程中执行响应方法。如果发布线程就是主线程,则直接调用订阅者的事件响应方法,否则通过主线程的 Handler 发送消息在主线程中处理——调用订阅者的事件响应函数。显然,MainThread类的方法也不能有耗时操作,以避免卡主线程。适用场景:必须在主线程执行的操作;
  3. BackgroundThread:在后台线程中执行响应方法。如果发布线程不是主线程,则直接调用订阅者的事件响应函数,否则启动唯一的后台线程去处理。由于后台线程是唯一的,当事件超过一个的时候,它们会被放在队列中依次执行,因此该类响应方法虽然没有PostThread类和MainThread类方法对性能敏感,但最好不要有重度耗时的操作或太频繁的轻度耗时操作,以造成其他操作等待。适用场景:操作轻微耗时且不会过于频繁,即一般的耗时操作都可以放在这里;
  4. Async:不论发布线程是否为主线程,都使用一个空闲线程来处理。和BackgroundThread不同的是,Async类的所有线程是相互独立的,因此不会出现卡线程的问题。适用场景:长耗时操作,例如网络访问。

这里我们可以看到从最开始eventbus就通过反射将需要调用的函数加载到eventbus的类里保存下来了。不过这里也可以知道其实eventbus也只是通过handler来调用主界面的线程。秘密揭开了,自己也可以尝试写一套。