前面已经对EventBus订阅者注册注销源码进行了分析,没看过的朋友可以先看:Android EventBus3源码解析(上),本文来解析EventBus3的事件发送原理
post方式发送事件
post发送事件的基本操作如下
EventBus.getDefault().post(new StrMessage("incoming message"));
跳进去看看!
/** Posts the given event to the event bus. */
public void post(Object event) {
//1
PostingThreadState postingState = currentPostingThreadState.get();
List<Object> eventQueue = postingState.eventQueue;
//2
eventQueue.add(event);
if (!postingState.isPosting) {
postingState.isMainThread = isMainThread();
postingState.isPosting = true;
if (postingState.canceled) {
throw new EventBusException("Internal error. Abort state was not reset");
}
try {
//3
while (!eventQueue.isEmpty()) {
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
代码不长,首先看代码1处。因为post方法可以在子线程调用也可以在主线程调用,为了避免线程间的干扰,所以postingState是通过ThreadLocal来获取的。postingState字面意思就是当前发送的状态,在不同线程的下状态也可能不同,所以要用ThreadLocal。
代码2处把传进来的事件加入到队列中,排队等待发送。
到了代码块3处,就把事件队列里的事件全都发出去。进去postSingleEvent方法看看。
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
//A
if (eventInheritance) {
List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
int countTypes = eventTypes.size();
for (int h = 0; h < countTypes; h++) {
Class<?> clazz = eventTypes.get(h);
subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
}
} else {
subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
}
//B
if (!subscriptionFound) {
if (logNoSubscriberMessages) {
logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
}
if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
eventClass != SubscriberExceptionEvent.class) {
//C
post(new NoSubscriberEvent(this, event));
}
}
}
其实代码可以整体分成两大块,A:有该事件的订阅者,B:没有该事件的订阅者。B比较简单,先分析它。
我们在使用EventBus时可能发送了一个事件,但这个事件却没有订阅者,这时EventBus就会把这类事件定为 “没人订阅的事件” 并发出去,在代码C处。当然,发出去以后,B代码块就又走了一遍,不过这回C代码不会再运行了。
回过头来再分析一下有订阅者的情况,也就是A代码块。eventInheritance是指继承事件,如果为false那就只发送本事件,如果为true,那么连同该事件类的父类事件也会被发送。通过lookupAllEventTypes方法就可以获取该事件类型及其父事件类型。
深入postSingleEventForEventType方法才能知道是怎么发送的。不过在此之前可看到这个方法会返回一个boolean值给subscriptionFound 。其实就是在看这个事件发出去有没有订阅者接收。
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
//1
subscriptions = subscriptionsByEventType.get(eventClass);
}
if (subscriptions != null && !subscriptions.isEmpty()) {
//2
for (Subscription subscription : subscriptions) {
postingState.event = event;
postingState.subscription = subscription;
boolean aborted = false;
try {
//3
postToSubscription(subscription, event, postingState.isMainThread);
//如果EventBus.getDefault().cancelEventDelivery(event)被调用
//那么aborted就为ture
aborted = postingState.canceled;
} finally {
postingState.event = null;
postingState.subscription = null;
postingState.canceled = false;
}
//4
if (aborted) {
break;
}
}
return true;
}
return false;
}
代码1处就是在看有没有这类事件的订阅者,没有的话就直接返回false。
否则就在2的循环里一个一个地通知。已经很明显了,上文猜测EventBus使用了观察者模式现在得到了证实。
在不被订阅者截断事件的情况下,每一个该事件的订阅者都会在postToSubscription方法中响应该事件。
如果有订阅者调用在处理事件时调用了EventBus.getDefault().cancelEventDelivery(event)那么该事件就会被截断,后面的订阅者就接收不到了,也就是在代码4处结束的。
再深入到postToSubscription方法里面看看是怎么让订阅者接收并处理事件的。
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {
//1
case POSTING:
invokeSubscriber(subscription, event);
break;
//2
case MAIN:
if (isMainThread) {
invokeSubscriber(subscription, event);
} else {
mainThreadPoster.enqueue(subscription, event);
}
break;
//3
case MAIN_ORDERED:
if (mainThreadPoster != null) {
mainThreadPoster.enqueue(subscription, event);
} else {
// temporary: technically not correct as poster not decoupled from subscriber
invokeSubscriber(subscription, event);
}
break;
//4
case BACKGROUND:
if (isMainThread) {
backgroundPoster.enqueue(subscription, event);
} else {
invokeSubscriber(subscription, event);
}
break;
//5
case ASYNC:
asyncPoster.enqueue(subscription, event);
break;
default:
throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
}
}
整个方法就是一个switch代码块,上面的分支就是我们熟悉的threadMode,在给订阅者写接收事件方法的注解时就会用到这些threadMode,默认下是POSTING方式。不过现在全部都分析一遍。
首先是POSTING,在代码1处,注释解释道POSTING方法下哪个线程发的,订阅者的接收方法就在哪个线程上执行。再跳进invokeSubscriber看看。
void invokeSubscriber(Subscription subscription, Object event) {
try {
subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
} catch (InvocationTargetException e) {
handleSubscriberException(subscription, event, e.getCause());
} catch (IllegalAccessException e) {
throw new IllegalStateException("Unexpected exception", e);
}
}
已经非常明显了,至此,订阅者的接收方法就真正地被调用了。果然是立马就在当前线程下调用的。
再看看代码块2,是MAIN方式,其实很简单,就是让订阅者接收方法在主线程上执行,(这里也再啰嗦一句,不要在主线程上做耗时的任务)。
接下来是代码块3,MAIN_ORDERED方式,它和MAIN方式有什么区别呢?其实可以看看注释。
/**
* On Android, subscriber will be called in Android's main thread (UI thread). If the posting thread is
* the main thread, subscriber methods will be called directly, blocking the posting thread. Otherwise the event
* is queued for delivery (non-blocking). Subscribers using this mode must return quickly to avoid blocking the main thread.
* If not on Android, behaves the same as {@link #POSTING}.
*/
MAIN
/**
* On Android, subscriber will be called in Android's main thread (UI thread). Different from {@link #MAIN},
* the event will always be queued for delivery. This ensures that the post call is non-blocking.
*/
MAIN_ORDERED
大致意思就是说,Main方式下,如果是在主线程下post的,那就立马执行,如果是在子线程下post的就放在事件队列中排队执行。
而在MAIN_ORDERED方式下,无论在哪个线程下post,都要排队等待处理。还是有点区别的。
接下来是代码块5,BACKGROUND方式。代码很简单,就是说如果是在主线程下post的,就把事件处理放到一个后台处理队列排队等待处理。如果是在子线程下post的,那就立马执行。
上文多次提到把事件放进事件队列,但是光放进去还不够,还得有谁来处理它们。跳进去enqueue方法看看能不能找到这个处理者。
//1
final class BackgroundPoster implements Runnable, Poster {
...
public void enqueue(Subscription subscription, Object event) {
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
synchronized (this) {
queue.enqueue(pendingPost);
if (!executorRunning) {
executorRunning = true;
//2
eventBus.getExecutorService().execute(this);
}
}
}
...
//3
@Override
public void run() {
try {
try {
while (true) {
//4
PendingPost pendingPost = queue.poll(1000);
if (pendingPost == null) {
synchronized (this) {
// Check again, this time in synchronized
pendingPost = queue.poll();
if (pendingPost == null) {
executorRunning = false;
return;
}
}
}
//5
eventBus.invokeSubscriber(pendingPost);
}
} catch (InterruptedException e) {
eventBus.getLogger().log(Level.WARNING, Thread.currentThread().getName() + " was interruppted", e);
}
} finally {
executorRunning = false;
}
...
跳进enqueue方法,再看到代码2的这个地方,然后再看看代码1这个地方。现在应该很清楚了,原来BACKGROUND处理事件是放在子线程中去做的,而这些子线程又是通过线程池来统一管理的(而不是简单粗暴地new Thread().start())。
具体怎么处理队列的呢?看到代码块3的run方法就很清楚了。每次有一秒钟的等待时间,如果等了这么久还没有事件入列,就返回,这个子线程就结束了,让线程池来处理后事。如果事件队列不为空,订阅者的接收方法就在这里被调用了。由于是while(true)死循环,所以这个子线程可以处理多个事件。
至此,BACKGROUND方式也就分析完毕了。回过头来再看看最后一种方式。
...
//5
case ASYNC:
asyncPoster.enqueue(subscription, event);
break;
...
其实对比一下BACKGROUND方式就会发现ASYNC方式与之很相似,都会避免在主线程下执行,只是说ASYNC方式下,无论是否在子线程下post,事件的的处理都会放到新的子线程去处理。同样,在这个方式下子线程也是通过线程池来管理的。
到这里post操作就分析完了。
postSticky方式发送事件
使用方法如下
EventBus.getDefault().postSticky(new StrMessage("incoming message"));
跳进去看看。
/**
* Posts the given event to the event bus and holds on to the event (because it is sticky). The most recent sticky
* event of an event's type is kept in memory for future access by subscribers using {@link Subscribe#sticky()}.
*/
public void postSticky(Object event) {
synchronized (stickyEvents) {
stickyEvents.put(event.getClass(), event);
}
// Should be posted after it is putted, in case the subscriber wants to remove immediately
post(event);
}
代码就比post多了一步,把粘性事件保存在了一个HashMap里。但粘性事件是什么时候被接收的?其实在上文已经解析了一大半了,这里再简单回顾一下。
当初在Activity注册订阅者的过程中,就已经在监听与之相关的粘性事件了。
...
if (subscriberMethod.sticky) {
if (eventInheritance) {
// Existing sticky events of all subclasses of eventType have to be considered.
// Note: Iterating over all events may be inefficient with lots of sticky events,
// thus data structure should be changed to allow a more efficient lookup
// (e.g. an additional map storing sub classes of super classes: Class -> List<Class>).
Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
for (Map.Entry<Class<?>, Object> entry : entries) {
Class<?> candidateEventType = entry.getKey();
if (eventType.isAssignableFrom(candidateEventType)) {
Object stickyEvent = entry.getValue();
//1
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
} else {
Object stickyEvent = stickyEvents.get(eventType);
//2
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
...
代码1和代码2就是在查看是不是有与订阅者相关的粘性事件还留着,有的话就拿来处理。看看checkPostStickyEventToSubscription方法。
private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {
if (stickyEvent != null) {
// If the subscriber is trying to abort the event, it will fail (event is not tracked in posting state)
// --> Strange corner case, which we don't take care of here.
postToSubscription(newSubscription, stickyEvent, isMainThread());
}
}
果然是postToSubscription方法,这就又回到了post的路上了。如此一来,刚注册的订阅者就能收到粘性事件了。
本文讲解了事件的收发,剩下的部分留到Android EventBus3源码解析(下)来讲解。
参考文章
eventInheritance的含义:https://blog.csdn.net/wangshihui512/article/details/50947102