EventBus3.0源码分析
一、EventBus的使用
二、源码分析
EventBus的使用一般分三步,1、注册、订阅,2、事件发送,3、解绑
注册分析
EventBus.getDefault().register(this),EventBus采用了单例模式
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
register(this)注册到底做了什么?
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
findSubscriberMethods(subscriberClass)这个方法从字面上我们就可以知道它的作用是拿到我们订阅的method,即使用注解@subscribe标注的方法。SubscriberMethod类包含一些方法的信息
public SubscriberMethod(Method method, Class<?> eventType, ThreadMode threadMode, int priority, boolean sticky) {
this.method = method;
this.threadMode = threadMode;
this.eventType = eventType;
this.priority = priority;
this.sticky = sticky;
}
这个类包含线程模式、事件类型、优先级、是否是粘性事件,EventBus是如何获取被注解的方法信息的呢?有两种方法,1、通过反射获取,2、编译时注解生成索引,通过ignoreGeneratedIndex变量来判断是否使用索引
1、通过放射
private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
findUsingReflectionInSingleClass(findState);
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}
findUsingReflection()会将subscriberClass类本身以及它的父类中的方法都遍历一遍,找到使用@subscribe注解的方法封装成一个SubscriberMethod对象。findUsingReflectionInSingleClass(findState)就是寻找findState.clazz 中注解的方法,结束后就将findState.clazz 的父类赋值给自身接着跑循环,所以最终父类注解的方法也被挑出来了。我们都知道反射会影响程序的性能,那么EventBus是如何解决的了?这就有了第二种方法
2、编译时注解添加索引,解决反射的性能问题
(https://www.jianshu.com/p/96b198244cb5)
回到注册register(),获取到所有SubscriberMethod信息后,遍历它逐一调用subscribe(subscriber, subscriberMethod),subscribe()做了三件事,
1、使用订阅者和订阅者的方法信息subscriberMethod生成Subscription对象,按优先级存入subscriptionsByEventType中
2、记录订阅者的事件类型于typesBySubscriber
3、判断subscriberMethod是否是粘性事件,是的话就将stickyEvents中存放的事件粘性事件投放给subscriberMethod
事件发送
发送事件分两种,1、post(),2、postSticky()发送粘性事件
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);
}
postSticky()其实也是调用了post(),只是多了一步将事件存入stickyEvents中
。stickyEvents如果不手动remove的话,会一直存在,前面注册的时候我们知道会触发粘性事件,所以后注册的订阅者只要有stickyEvents中对应的事件订阅还是会收到的。
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;
}
}
}
先将event存入eventQueue,然后遍历它postSingleEvent(eventQueue.remove(0), postingState),最终会调用postToSubscription()真正的触发订阅方法
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {
case POSTING:
invokeSubscriber(subscription, event);
break;
case MAIN:
if (isMainThread) {
invokeSubscriber(subscription, event);
} else {
mainThreadPoster.enqueue(subscription, event);
}
break;
case BACKGROUND:
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);
}
}
调用invokeSubscriber(subscription, event)就会触发订阅方法,通过反射完成。我们这里的重点分析下它是如何切换线程的,这里有三个poster,mainThreadPoster、backgroundPoster、asyncPoster分别处理主线程、后台线程(非主线程则直接调用,主线程则开单线程调用)、异步线程(异步调用,使用线程池在不同的线程调用)。
mainThreadPoster这个没啥好说的了,主线程Handler,其余两个都实现了Runnable且持有eventBus实例,主要区别就在于他们的run()实现,backgroundPoster只要一个线程中处理queue中的所有事件,queue结束为止,而asyncPoster会每次enqueue()都调用eventBus.getExecutorService().execute(this)也就是说所有事件可能在不同的线程执行(线程池复用线程),它们最终只是改变了eventBus.invokeSubscriber(pendingPost)的调用线程。
解绑
List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
if (subscribedTypes != null) {
for (Class<?> eventType : subscribedTypes) {
unsubscribeByEventType(subscriber, eventType);
}
typesBySubscriber.remove(subscriber);
} else {
Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
解绑只是将订阅者监听的事件类型以及相应的subscriptionsByEventType清除