学习EventBus之发布事件

上篇文章中,我们从源码里了解了EventBus的注册机制。这篇文章我们接着上篇,把另一半——发布来了解一下。

我们一般可以通过eventBus.getDefault().post(event)的方式来发布一个事件,然后订阅了这个类型事件的类就会接收到这个事件并进行相应的处理。那么,我们就从post(event)下手,看看post的原理是什么样子的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/** Posts the given event to the event bus. */
public void post(Object event) {
//获取当前发布线程一个线程局部变量,这个变量存储了
//发布事件,事件订阅者以及发布是否是主线程等信息
PostingThreadState postingState = currentPostingThreadState.get();
//当前线程的事件队列
List<Object> eventQueue = postingState.eventQueue;
//将新发布的事件加入队列
eventQueue.add(event);

if (!postingState.isPosting) {
//当前发布的线程是否在主线程
postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper();
//更改发布状态
postingState.isPosting = true;
if (postingState.canceled) {
//这个状态会在发布成功后重置为false
throw new EventBusException("Internal error. Abort state was not reset");
}
try {
while (!eventQueue.isEmpty()) {//事件队列不为空 推送队列头的事件 并从队列中移除该事件
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
//结束发布 重置推送状态参数
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}

可以看到在当前线程里有个线程局部变量PostingThreadState,这个对象里记录了当前线程的事件队列,是否主线程以及事件的订阅者等信息。

1
2
3
4
5
6
7
8
9
10
11
final static class PostingThreadState {
//事件队列
final List<Object> eventQueue = new ArrayList<Object>();
boolean isPosting;
boolean isMainThread;
//订阅者
Subscription subscription;
//推送的事件
Object event;
boolean canceled;
}

新发布的事件会添加到事件队列中,然后从队列头部拿出一个事件调用postSingleEvent()方法进行发布。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
if (eventInheritance) {//是否发布有继承关系类型的事件
//查找与该类事件有关的所有类型,包括事件类型本身,它的父类以及它实现的接口
List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
int countTypes = eventTypes.size();
for (int h = 0; h < countTypes; h++) {//这里进行便利推送事件,调用post一次其实推送了好多个事件
Class<?> clazz = eventTypes.get(h);
subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
}
} else {
//如果不发布有继承关系的事件,则只发布一个该类型的事件
subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
}
if (!subscriptionFound) {
//没有注册的接收该类事件的对象,跑出异常
if (logNoSubscriberMessages) {
Log.d(TAG, "No subscribers registered for event " + eventClass);
}
if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
eventClass != SubscriberExceptionEvent.class) {
post(new NoSubscriberEvent(this, event));
}
}
}

private List<Class<?>> lookupAllEventTypes(Class<?> eventClass) {
synchronized (eventTypesCache) {
List<Class<?>> eventTypes = eventTypesCache.get(eventClass);
if (eventTypes == null) {
eventTypes = new ArrayList<Class<?>>();
Class<?> clazz = eventClass;
while (clazz != null) {
eventTypes.add(clazz);//事件类型
addInterfaces(eventTypes, clazz.getInterfaces());//事件类实现的接口类型
clazz = clazz.getSuperclass();//事件类的父类
}
eventTypesCache.put(eventClass, eventTypes);
}
return eventTypes;
}
}

这里有一个eventTypesCache的集合,该集合内以事件类型为key,以该事件类型的父类、接口以及自己为value存储了该类事件的所有父类和接口的Class类型。当post一个新事件的时候,首先根据该事件的类型查找之前有没有发送过同样的事件,如果没有则存起来,方便下次使用。最后调用postSingleEventForEventType()方法把该类型的事件发布出去。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
subscriptions = subscriptionsByEventType.get(eventClass);//根据事件的类型从订阅列表中查找该类事件的订阅者
}
if (subscriptions != null && !subscriptions.isEmpty()) {//查找到了该类事件的订阅者 最终会返回True 如果没查到则返回false
for (Subscription subscription : subscriptions) {//遍历查找到的订阅者
postingState.event = event;
postingState.subscription = subscription;
boolean aborted = false;
try {
//拿到订阅者和事件,发布事件
postToSubscription(subscription, event, postingState.isMainThread);
aborted = postingState.canceled;
} finally {
postingState.event = null;
postingState.subscription = null;
postingState.canceled = false;
}
if (aborted) {
break;
}
}
return true;
}
return false;
}

这里我们看到了上篇文章中出现的subscriptionsByEventType,从上篇文章我们知道这个集合里根据事件类型存储了订阅某类事件的所有类。所以这里首先根据事件类型拿到所有的该类型事件的订阅者,然后遍历调用postToSubscription()方法,该方法通过反射的方式调用订阅者内的对应onEvent**方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {//根据订阅者中订阅方法的线程模式进行不同的调用
case PostThread://如果订阅者的订阅方法线程模式是PostThread,则直接在推送线程中调用订阅方法
invokeSubscriber(subscription, event);
break;
case MainThread:
if (isMainThread) {//如果订阅者的订阅方法线程模式是MainThread,且事件推送线程也是MainThread,则直接调用订阅方法
invokeSubscriber(subscription, event);
} else {//否则发送到主线程的队列中进行调用
mainThreadPoster.enqueue(subscription, event);
}
break;
case BackgroundThread:
if (isMainThread) {//如果订阅者的订阅方法线程模式是BackgroundThread,且事件推送线程也是MainThread,则发送到BackgroundThread的队列中调用
backgroundPoster.enqueue(subscription, event);
} else {//推送事件不在主线程,则直接调用
invokeSubscriber(subscription, event);
}
break;
case Async://异步调用,把事件加入到异步线程的队列中进行调用
asyncPoster.enqueue(subscription, event);
break;
default:
throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
}
}

可以看到,这里根据订阅者的方法名判断的方法应该在哪个线程模式下执行,分了4个分支。注释中对这几种模式的订阅者方法回调方式已经做了说明。

不管是那种线程模式,最终都会调用invokeSubscriber(Subscription subscription, Object event)方法。

1
2
3
4
5
void invokeSubscriber(Subscription subscription, Object event) {

subscription.subscriberMethod.method.invoke(subscription.subscriber, event);

}

显然,这里使用了反射的方法来回调订阅者中的事件处理方法。至此,一个订阅/发布以及处理的流程就完成了。

当然,我们这里没有分析EventBus的高级使用方法,包括事件的阻断、粘滞事件等等,有兴趣的朋友可以自己研究下。