您的位置:首页 > 其它

EventBus源码解读详细注释(6)从事件发布到事件处理,究竟发生了什么!

2016-03-21 20:45 681 查看


[EventBus源码分析(一):入口函数提纲挈领(2.4版本)](http://blog.csdn.net/wangshihui512/article/details/51802172)
[EventBus源码分析(二):register方法保存事件的订阅者列表(2.4版本)](http://blog.csdn.net/wangshihui512/article/details/51819508)
[EventBus源码分析(三):post方法发布事件【获取事件的所有订阅者,反射调用订阅者事件处理方法】(2.4版本)](http://blog.csdn.net/wangshihui512/article/details/51821143)
[EventBus源码分析(四):线程模型分析(2.4版本)](http://blog.csdn.net/wangshihui512/article/details/51832001)
[EventBus源码解读详细注释(1)register的幕后黑手](http://blog.csdn.net/wangshihui512/article/details/50914817)
[EventBus源码解读详细注释(2)MainThread线程模型分析](http://blog.csdn.net/wangshihui512/article/details/50934012)
[EventBus源码解读详细注释(3)PostThread、MainThread、BackgroundThread、Async四种线程模式的区别](http://blog.csdn.net/wangshihui512/article/details/50935729)
[EventBus源码解读详细注释(4)register时刷新的两个map](http://blog.csdn.net/wangshihui512/article/details/50938663)
[EventBus源码解读详细注释(5)事件消息继承性分析 eventInheritance含义](http://blog.csdn.net/wangshihui512/article/details/50947102)
[EventBus源码解读详细注释(6)从事件发布到事件处理,究竟发生了什么!](http://blog.csdn.net/wangshihui512/article/details/50949960)

EventBus内部存储了事件到此事件所有订阅者的map,因此post事件的时候,可以找到订阅了此事件的所有订阅者,然后根据订阅者对此事件的线程模型,在对应的线程里边,通过反射调用事件处理函数。

先看EventBus里边两个重要的map

/*下边的两个map的数据都是各个线程都可以访问的,因此访问的时候要对这两个map加锁*/
/*事件到订阅者列表的map,key是事件,也就是消息处理方法的参数的Class,value是所有的订阅此事件的订阅者列表*/
private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
/*订阅者到订阅者订阅的所有事件列表的map,key是订阅者,value是该订阅者订阅的所有事件的列表*/
private final Map<Object, List<Class<?>>> typesBySubscriber;

正是这两个map存储了事件和订阅者之间对应关系的重要信息。

然后从源码的角度查看post事件的时候,事件处理方法是如何被调用的

/**EventBus维护了两个重要的map,其中一个就是事件到所有订阅了此事件的订阅者所构成的列表的map
* 因此post一个事件,总能根据此map找到所有的订阅者,再根据订阅者处理此事件的线程模型,将此事件
* 分发到对应的线程,利用反射,调用订阅者对此事件的事件处理方法,完成事件的发布与处理
* Posts the given event to the event bus.
* @param event*/
public void post(Object event) {
/* private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>()
* ThreadLocal,每个线程独有,当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,
* 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。*/
/*获取发布者线程的PostingThreadState,PostingThreadState封装了一些发布者线程的数据*/
PostingThreadState postingState = currentPostingThreadState.get();
/*获取该发布者线程的事件列表*/
List<Object> eventQueue = postingState.eventQueue;
/*将新post的event添加到发布者线程的事件列表*/
eventQueue.add(event);
/*判断标志位,判断发布者线程是否正在post*/
if (!postingState.isPosting) {
/** Looper.getMainLooper():
* Returns the application's main looper, which lives in the main thread of the application.
* Looper.myLooper:
* Return the Looper object associated with the current thread.
*/
/*判断发布者线程是否为UI线程*/
postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper();
/*设置标志位,防止重复进入。假如发布者正在发布post,此时发布者又post了一个事件
* 虽然这时候不会通过 if (!postingState.isPosting)的判断
* 但是eventQueue.add(event);已经把此事件加入列表,在下边的循环中还是可以处理此event*/
postingState.isPosting = true;
/*如果这时候发布者取消发布,就抛出异常*/
if (postingState.canceled) {
throw new EventBusException("Internal error. Abort state was not reset");
}
/*循环post事件,将发布者发布的所有事件都发布出去
* 比如在某个Activity里边post了好几个事件,每次post都会将事件入队列
* */
try {
while (!eventQueue.isEmpty()) {
/** eventQueue.remove(0):
* Removes the object at the specified location from this {@code List}.
* @param location the index of the object to remove.
* @return the removed object.
* 取队首事件*/
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
/*设置标志位,不管是发布的时候出现问题,还是发布者的所有事件发布完毕,都会执行
* 设置正在发布状态为false,设置发布者线程为主线程标志为false
* 如果发布者此后又post了一个事件,那么就把此事件加入队列
* 重新判断发布者线程是否为主线程*/
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
PostingThreadState封装了一些发布者线程的数据状态

/** For ThreadLocal, much faster to set (and get multiple values). */
/*封装了订阅者、事件、事件队列、和三个标志位*/
final static class PostingThreadState {
/*发布者线程发布的事件列表,这里已经为列表分配了内存,所以直接调用add不会导致空指针*/
final List<Object> eventQueue = new ArrayList<Object>();
/*发布者是否正在发布事件*/
boolean isPosting;
/*发布者线程是否是主线程*/
boolean isMainThread;
/*发布事件的订阅者*/
Subscription subscription;
/*待发布的事件*/
Object event;
/*是否取消发布*/
boolean canceled;
}


/**
* @param event
* @param postingState
* @throws Error
*/
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
/*获取事件的Class,所有事件的Class对应的订阅者列表在register的时候是已经保存了的*/
Class<?> eventClass = event.getClass();
/*该事件是否有订阅者的标志状态,初始化为false*/
boolean subscriptionFound = false;
/*比如 A extends B implements C  发布者post(A),那么找订阅者的时候不仅要找订阅了事件A的订阅者
* 还要找订阅了B和C的订阅者*/
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);
}
/*此事件没有订阅者*/
if (!subscriptionFound) {
if (logNoSubscriberMessages) {
Log.d(TAG, "No subscribers registered for event " + eventClass);
}
/*如果设置了没有订阅者也照样发布事件并且该事件不是NoSubscriberEvent
或者SubscriberExceptionEvent类型
那么就发布一个特殊的事件类型*/
if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
eventClass != SubscriberExceptionEvent.class) {
post(new NoSubscriberEvent(this, event));
}
}
}

/**
* @param subscription
* @param event
* @param isMainThread
*/
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {
case PostThread:
/*在发布者的线程里边通过反射执行事件处理方法*/
invokeSubscriber(subscription, event);
break;
case MainThread:
/*如果发布者线程是主线程,直接在主线程里边通过反射执行事件处理方法*/
if (isMainThread) {
invokeSubscriber(subscription, event);
/*如果发布者线程不是主线程,就把此事件加入主线程消息循环处理队列,在主线程中
* 通过反射调用事件处理方法*/
} else {
mainThreadPoster.enqueue(subscription, event);
}
break;
case BackgroundThread:
/*如果发布者线程是主线程,那么就把此事件加入后台线程消息循环处理队列
* 通过反射调用事件处理方法,此线程模式多个事件都在一个后台线程中循环处理
* 通过队列管理多个事件*/
if (isMainThread) {
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);
}
}
四种线程模型之前已经分析过了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: