EventBus源码分析(一):入口函数提纲挈领(2.4版本)
EventBus源码分析(二):register方法保存事件的订阅者列表(2.4版本)
EventBus源码分析(三):post方法发布事件【获取事件的所有订阅者,反射调用订阅者事件处理方法】(2.4版本)
EventBus源码分析(四):线程模型分析(2.4版本)
EventBus源码解读详细注释(1)register的幕后黑手
EventBus源码解读详细注释(2)MainThread线程模型分析
EventBus源码解读详细注释(3)PostThread、MainThread、BackgroundThread、Async四种线程模式的区别
EventBus源码解读详细注释(4)register时刷新的两个map
EventBus源码解读详细注释(5)事件消息继承性分析 eventInheritance含义
EventBus源码解读详细注释(6)从事件发布到事件处理,究竟发生了什么!
EventBus维护了一个重要的HashMap,这个HashMap的键是事件,值是该事件的订阅者列表,因此post事件的时候就能够从此HashMap中取出事件的订阅者列表,对每个订阅者反射调用事件处理方法。
private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
post方法:发布者线程事件排队
/** 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) {
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;
}
}
}
因为EventBus发布事件的线程多样性,可能是主线程,可能是线程池分配的线程,也可能是临时非配的一个线程。EventBus发布事件和处理事件可能会线程切换,因此需要记录发布者线程的状态。
PostingThreadState:记录发布者线程状态
PostingThreadState类正是记录了发布者的状态
/** For ThreadLocal, much faster to set (and get multiple values). */
final static class PostingThreadState {
final List<Object> eventQueue = new ArrayList<Object>();
boolean isPosting;
boolean isMainThread;
Subscription subscription;
Object event;
boolean canceled;
}
可见PostingThreadState为给个线程设置了一个事件队列,一个事件,一个封装好的订阅者Subscription对象和三个标志位(最主要的信息就是发布者线程是否为主线程,随后会根据发布者线程是否为主线程决策是否要线程切换)。
Java中为每个线程设置不同的数据使用到的是ThreadLocal,EventBus通过ThreadLocal为每个线程创建一个PostingThreadState对象并保存发布者的状态。
private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
@Override
protected PostingThreadState initialValue() {
return new PostingThreadState();
}
};
post方法中首先获取了发布者线程的PostingThreadState对象,获取该线程对象的事件队列,并把这次发布的事件添加到发布者线程的事件队列中。
PostingThreadState postingState = currentPostingThreadState.get();
List<Object> eventQueue = postingState.eventQueue;
eventQueue.add(event);
然后通过循环,取出发布者线程的事件队列的每一个事件,进行发布,一直到队列为空。
如果正在循环处理事件队列,进行事件发布,那么发布者线程的isPosting属性为真,这时候再次调用post发布事件,只会进队列,不会再次循环,因为之前的循环会处理队列中的事件,包括后来入队列的事件。事件队列此时为一个生产者消费者模型,EventBus消费事件,用户通过post生产事件。
postingState.isPosting = true;
因此post发布事件的时候,会先获取发布者线程的信息(最重要的线程信息是发布者线程是否为主线程isMainThread),然后对发布者线程发布的每一个事件进行排队,通过postSingleEvent方法对队列中的每一个事件进行处理,postSingleEvent方法需要知道发布者线程是否为主线程,通过传入发布者线程的ThreadLocal的PostingThreadState对象即可。
postSingleEvent:事件继承性分析
如果post(A),A extends B implements C
那么onEvent(A)、onEvent(B)、onEvent(C)这三个个事件处理方法那些能得到调用呢
答案是onEvent(A)、onEvent(B)、onEvent(C)这三个事件处理方法都会得到调用。
EventBus有个属性eventInheritance专门用来控制是否要处理事件的父类,默认值是true,表示默认是要处理事件父类的。
postSingleEvent对是否要处理事件的父类进行了分类讨论。
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++) {
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));
}
}
}
如果不需要处理事件父类的话,那么直接调用postSingleEventForEventType对当前的事件进行发布处理即可。
否则,需要找到当前发布事件的所有父类(包括实现的接口),对父类也通过postSingleEventForEventType方法进行处理。
如何获取一个类的所有父类和实现的接口
...
private static final Map<Class<?>, List<Class<?>>> eventTypesCache = new HashMap<Class<?>, List<Class<?>>>();
...
/** Looks up all Class objects including super classes and interfaces. Should also work for interfaces. */
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;
}
}
EventBus对于缓存的设计是非常的精辟准确。获取一个类的所有的父类和实现的接口这一操作,是比较耗费性能的,为了更快的获取,采用缓存策略,以空间换时间,缓存同样通过静态的HashMap实现。
lookupAllEventTypes方法中同样是先获取缓存,只有在缓存不命中的情况下才进行查找,查找的过程就是向上循环迭代,遇到根类Object停止迭代 。
对应所实现的接口的处理,则是对每一层,进行递归获取
/** Recurses through super interfaces. */
static void addInterfaces(List<Class<?>> eventTypes, Class<?>[] interfaces) {
for (Class<?> interfaceClass : interfaces) {
if (!eventTypes.contains(interfaceClass)) {
eventTypes.add(interfaceClass);
addInterfaces(eventTypes, interfaceClass.getInterfaces());
}
}
}
postSingleEventForEventType:获取事件的订阅者列表,对每个订阅者进行事件处理
最开始EventBus维护的事件到订阅者列表的map终于派上用场了,从该map中获取待发布事件的所有订阅者,对每个订阅者都要进行事件处理。
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
subscriptions = subscriptionsByEventType.get(eventClass);
}
if (subscriptions != null && !subscriptions.isEmpty()) {
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;
}
postSingleEventForEventType方法首先在同步代码块中获取事件的订阅者列表
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
subscriptions = subscriptionsByEventType.get(eventClass);
}
对每一个订阅者进行处理的方法需要传入一个发布者线程的信息,其实EventBus只关心发布者线程是否为主线程,因为根据不同的线程模型可能会线程切换。
for (Subscription subscription : subscriptions) {
...
postToSubscription(subscription, event, postingState.isMainThread);
...
}
postToSubscription:线程切换
在postToSubscription方法中,EventBus根据事件的线程模式和发布者线程是否为主线程,在规定的线程中对事件处理方法进行反射调用
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);
}
}
- PostThread模式不需线程切换,直接在发布者线程进行事件处理。
- MainThread模式分类讨论:发布者线程是主线程则直接调用事件处理方法,否则通过Handler进行线程切换。
- BackgroundThread模式分类讨论:发布这线程不是主线程则在发布者线程直接处理事件,否则线程切换至线程池处理。
- Async模式不关心发布者线程直接在线程池中开辟一个新的线程处理事件。
具体的线程模型分析将会在下一篇进行详细分析。
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);
}
}
这里通过invoke方法对事件处理方法进行了反射调用。invoke方法会可能会抛出三个异常,这里只处理了两个。IllegalArgumentException异常并没有捕获,因为在保存事件处理方法的时候已经做了约束,保存的事件处理方法肯定只有一个参数,因此肯定不会抛出非法参数异常。