相信大家都有在项目中使用过EventBus。EventBus是一个性能高效的基于观察者模式的事件发布与订阅框架。借助EventBus,我们只需几行代码便能实现组件之间、线程之间的通信,达到解耦的目的。
这篇文章不对EventBus的使用进行介绍,而是格物致知,探究EventBus的源码。
一般使用时是通过EventBus.getDefault()来调用注册、发布与注销的方法。因此,我们从getDefault()这个入口开始分析。
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
这里使用了双重检验锁(单例模式的一种实现方式)的来保证EventBus实例的唯一性。接着进入构造函数看看:
public EventBus() {
this(DEFAULT_BUILDER);
}
EventBus(EventBusBuilder builder) {
subscriptionsByEventType = new HashMap<>();
typesBySubscriber = new HashMap<>();
stickyEvents = new ConcurrentHashMap<>();
mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);
backgroundPoster = new BackgroundPoster(this);
asyncPoster = new AsyncPoster(this);
indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
builder.strictMethodVerification, builder.ignoreGeneratedIndex);
logSubscriberExceptions = builder.logSubscriberExceptions;
logNoSubscriberMessages = builder.logNoSubscriberMessages;
sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
throwSubscriberException = builder.throwSubscriberException;
eventInheritance = builder.eventInheritance;
executorService = builder.executorService;
}
在无参构造函数内调用了带有EventBusBuilder参数的构造函数,这里使用了Builder模式,将参数的构建过程交给EventBusBuilder。Builder模式是一种常见的设计模式,例如在sdk开发中参数过多时,将参数的构建过程交给Param:
Param param = Param.instance().param1(value1).param2(value2)......;
SDK.init(context, param);
在EventBus的构造函数内,进行了初始化的工作。这些参数的含义对后面的分析很重要,这里简单介绍下这些属性的作用:
- subscriptionsByEventType:以EventType(Event对象的Class类)为key,Subscription(订阅者以及一个订阅方法)数组为value。根据EventType可以查询到相应的订阅者及订阅方法。
- typesBySubscriber:以Subscriber(订阅者,即调用register方法的类)为key,EventType数组为value。根据订阅者可以查询到它订阅的所有事件。
- stickyEvents:以EventType为key,Event为value。用来保存粘性事件。
- mainThreadPoster、backgroundPoster、asyncPoster:EventBus支持四种ThreadMode,POSTING、MAIN、BACKGROUND、ASYNC。POSTING直接在发布事件的线程处理,这三个poster分别用来支持剩下的三种Mode。
- indexCount:索引类数目,索引类指的是EventBus利用注解处理器生成的保存订阅者信息的类。如果lib中也用了EventBus,就可能存在多个索引类。
- subscriberMethodFinder:查找及缓存订阅者信息的类。
- logSubscriberExceptions:当事件处理过程发生异常时是否打印日志,默认为true。
- logNoSubscriberMessages:当事件没有订阅者订阅时是否打印日志,默认为true。
- sendSubscriberExceptionEvent:当事件处理过程发生异常时是否发送SubscriberExceptionEvent,默认为true。当为true时,订阅者可以订阅SubscriberExceptionEvent事件。
- sendNoSubscriberEvent:当事件没有订阅者订阅时是否发送NoSubscriberEvent,默认为true。当为true时,订阅者可以订阅NoSubscriberEvent事件。
- throwSubscriberException:当事件处理过程发生异常时是否抛出EventBusException,默认为false。
- eventInheritance:是否支持事件继承,默认为true。当为true时,post一个事件A时,若A是B的子类或者A实现了接口B,订阅B的订阅者也能接收到事件。
- executorService:线程池,负责线程调度。
因此,通过EventBus.getDefault()我们就可以得到一个默认配置的EventBus单例,也支持通过EventBusBuilder来自定义配置。
接下来看下通过EventBus.getDefault().register()注册订阅者时发生了什么?
public void register(Object subscriber) {
Class> subscriberClass = subscriber.getClass();
List subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
当注册订阅者时,会通过SubscriberMethodFinder的findSubscriberMethods来获取SubscriberMethod数组,SubscriberMethod保存了订阅方法的信息。打开SubscriberMethod可以发现一点,在SubscriberMethod有Method对象以及methodString,其实methodString也是通过method反射获取到的,这里利用一个变量保存起来,避免每次都通过反射获取降低性能。类似的,在EventBus其实还有很多这种细微的优化。
得到SubscriberMethod数组后,依次进行注册。先看看获取到SubscriberMethod数组后,是如何通过subscribe进行订阅的:
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class> eventType = subscriberMethod.eventType;
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
CopyOnWriteArrayList subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<>();
subscriptionsByEventType.put(eventType, subscriptions);
} else {
if (subscriptions.contains(newSubscription)) {
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
+ eventType);
}
}
// 按优先级排序
int size = subscriptions.size();
for (int i = 0; i <= size; i++) {
if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
subscriptions.add(i, newSubscription);
break;
}
}
List> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
// 粘性事件处理, 将在第五小节分析
}
3-30行容易理解,主要是将EventType -> Subscription的映射加入到subscriptionsByEventType,将Subscriber -> EventType的映射与加入到typesBySubscriber中。subscriptionsByEventType里每个事件的Subscription是按照优先级排序的,优先级高的订阅者可以中途通过cancelEventDelivery来拦截。
31行后面部分的代码部分涉及到粘性事件,将在第五小节中分析。
回过头看看findSubscriberMethods是如何获取到SubscriberMethod数组的:
List findSubscriberMethods(Class> subscriberClass) {
List subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
if (ignoreGeneratedIndex) {
subscriberMethods = findUsingReflection(subscriberClass);
} else {
subscriberMethods = findUsingInfo(subscriberClass);
}
if (subscriberMethods.isEmpty()) {
throw new EventBusException("Subscriber " + subscriberClass
+ " and its super classes have no public methods with the @Subscribe annotation");
} else {
METHOD_CACHE.put(subscriberClass, subscriberMethods);
return subscriberMethods;
}
}
在findSubscriberMethods方法内,如果METHOD_CACHE中没有的话才进行查找的工作。然后根据ignoreGeneratedIndex判断查找的方式。
EventBus3.0里使用了一项“秘密武器”使得效率大大提升,这个武器其实就是之前文章里提过的注解处理器。如果ignoreGeneratedIndex是false的话,就采用注解处理器生成索引类去获取SubscriberMethod;否则采用反射的方式。
- butterknife源码分析:如何处理注解—反射与注解处理器
http://blog.csdn.net/u012933743/article/details/54972050
ignoreGeneratedIndex默认为false,因此会调用findUsingInfo:
private List findUsingInfo(Class> subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
findState.subscriberInfo = getSubscriberInfo(findState);
if (findState.subscriberInfo != null) {
SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
for (SubscriberMethod subscriberMethod : array) {
if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
findState.subscriberMethods.add(subscriberMethod);
}
}
} else {
findUsingReflectionInSingleClass(findState);
}
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}
在findUsingInfo开始时,会通过prepareFindState获取FindState对象,FindState保存了在查找SubscriberMethod时的一些属性,并封装了检验合法性的方法。prepareFindState里从对象池FIND_STATE_POOL里取一个已经创建好的对象,然后通过initForSubscriber去初始化。这也是一个细微的优化,利用对象池避免了频繁的对象创建。
private FindState prepareFindState() {
synchronized (FIND_STATE_POOL) {
for (int i = 0; i < POOL_SIZE; i++) {
FindState state = FIND_STATE_POOL[i];
if (state != null) {
FIND_STATE_POOL[i] = null;
return state;
}
}
}
return new FindState();
}
得到FindState后,先通过getSubscriberInfo获取订阅者信息SubscriberInfo:
private SubscriberInfo getSubscriberInfo(FindState findState) {
if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) {
SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo();
if (findState.clazz == superclassInfo.getSubscriberClass()) {
return superclassInfo;
}
}
if (subscriberInfoIndexes != null) {
for (SubscriberInfoIndex index : subscriberInfoIndexes) {
SubscriberInfo info = index.getSubscriberInfo(findState.clazz);
if (info != null) {
return info;
}
}
}
return null;
}
在getSubscriberInfo方法内,第一个判断其实进不去,因为findState.subscriberInfo.getSuperSubscriberInfo()始终返回null。然后直接SubscriberInfoIndex的getSubscriberInfo来获得订阅者信息SubscriberInfo。SubscriberInfoIndex就是采用注解处理器生成的索引类,包含了订阅者及其订阅方法的信息。
回到刚刚findUsingInfo的方法,得到SubscriberInfo后,获取订阅者内的SubscriberMethod数组。对于每个SubscriberMethod,调用FindState的checkAdd进行检验,如果检验通过的话,加入到FindState的subscriberMethods内。看看checkAdd方法对什么进行检验?
boolean checkAdd(Method method, Class> eventType) {
// 2 level check: 1st level with event type only (fast), 2nd level with complete signature when required.
// Usually a subscriber doesn't have methods listening to the same event type.
Object existing = anyMethodByEventType.put(eventType, method);
if (existing == null) {
return true;
} else {
if (existing instanceof Method) {
if (!checkAddWithMethodSignature((Method) existing, eventType)) {
// Paranoia check
throw new IllegalStateException();
}
// Put any non-Method object to "consume" the existing Method
anyMethodByEventType.put(eventType, this);
}
return checkAddWithMethodSignature(method, eventType);
}
}
private boolean checkAddWithMethodSignature(Method method, Class> eventType) {
methodKeyBuilder.setLength(0);
methodKeyBuilder.append(method.getName());
methodKeyBuilder.append('>').append(eventType.getName());
String methodKey = methodKeyBuilder.toString();
Class> methodClass = method.getDeclaringClass();
Class> methodClassOld = subscriberClassByMethodKey.put(methodKey, methodClass);
if (methodClassOld == null || methodClassOld.isAssignableFrom(methodClass)) {
// Only add if not already found in a sub class
return true;
} else {
// Revert the put, old class is further down the class hierarchy
subscriberClassByMethodKey.put(methodKey, methodClassOld);
return false;
}
}
checkAdd方法第一步先检验之前否有订阅过同一个EventType,如果有,在checkAddWithMethodSignature里再进一步检查key为methodName>eventTypeName的字符串是否存在。这里也是一个优化点,利用第一步检查,多数情况下避免了第二步利用了反射去获取methodName>eventTypeName对应的key。
通过这个校验我们知道两点:
① 允许一个类有多个方法名不同的方法对同个事件进行订阅。
② 如果子类重载了父类的订阅方法,以子类的为准。在checkAddWithMethodSignature检查method>eventTypeName时,如果methodClassOld为null或者methodClassOld为method为父类或本身时才返回true,说明子类重载这个订阅方法时,以子类的为准。
很多人认为在寻找SubscriberMethod时,是从子类到父类的,那isAssignableFrom似乎一直返回false,那意义是什么?其实这个作用主要发挥在反射时的,将在反射时介绍。
如果返回的SubscriberInfo为null的话,说明没有索引类。因此findUsingReflectionInSingleClass使用效率较低的反射方式。找到SubscriberMethod后,通过moveToSuperclass一层层向上寻找。最后getMethodsAndRelease重置FindState内的临时变量,并放置到对象池中。
如果ignoreGeneratedIndex为true时,采用反射的方式获取SubscriberMethod。而且如果使用了EventBus3.0,却不配置使用索引类,这样其实最后都会“降级”为反射。
值得注意的是,如果采用EventBus3.0却不使用注解处理器的方式效率是比EventBus2.4还要低的。因为EventBus2.4是固定订阅方法名为onEvent+ThreadMode的形式,而3.0方法名是自定义的,需要添加@Subscribe注解,相比之下多了反射获取注解的时间。
private List findUsingReflection(Class> subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
findUsingReflectionInSingleClass(findState);
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}
采用反射方式SubscriberMethod的流程也差不多,调用findUsingReflectionInSingleClass获取此类的SubscriberMethod后,一层层向上。最后释放FindState内的临时变量,并放置到对象池中。
看看如何通过findUsingReflectionInSingleClass获取一个类的SubscriberMethod:
private void findUsingReflectionInSingleClass(FindState findState) {
// 先获取类里面的所有方法。
Method[] methods;
try {
// This is faster than getMethods, especially when subscribers are fat classes like Activities
methods = findState.clazz.getDeclaredMethods();
} catch (Throwable th) {
// Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
methods = findState.clazz.getMethods();
findState.skipSuperClasses = true;
}
// 遍历Method,寻找包含@Subscribe注解的方法,并对合法性进行检测。
for (Method method : methods) {
int modifiers = method.getModifiers();
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
Class>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length == 1) {
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
if (subscribeAnnotation != null) {
Class> eventType = parameterTypes[0];
if (findState.checkAdd(method, eventType)) {
ThreadMode threadMode = subscribeAnnotation.threadMode();
findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
}
}
} else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
String methodName = method.getDeclaringClass().getName() + "." + method.getName();
throw new EventBusException("@Subscribe method " + methodName +
"must have exactly 1 parameter but has " + parameterTypes.length);
}
} else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
String methodName = method.getDeclaringClass().getName() + "." + method.getName();
throw new EventBusException(methodName +
" is a illegal @Subscribe method: must be public, non-static, and non-abstract");
}
}
}
在findUsingReflectionInSingleClass,先获取类里面的Method,通过getDeclaredMethods()或者getMethods()。这两个区别在于:getDeclaredMethods()返回这个类所有的方法,而getMethods()返回的是这个类及其父类的public的方法。因为在Activity这种继承关系很复杂的类里,getMethods()返回包含了很多系统的方法,大大降低了效率。
获取到Method数组后,校验包含@Subscribe注解的方法是不是为Public,是不是不含有abstract、static、bridge、synthtic修饰符,参数是不是只有一个。通过的话,再调用FindState的checkAdd校验,然后加到FindState的subscriberMethods里。
前面我们提到checkAdd内调用到的isAssignableFrom似乎没有意义,既然在寻找SubscriberMethod时,是一层层向上的,是不是isAssignableFrom一直返回false?其实不然,getMethods会得到这个类以及父类的public方法,因此isAssignableFrom是可能返回true的。
到这里注册订阅者的流程就结束了。
发送事件时,通过EventBus.getDefault.post(Event)来发送:
/** Posts the given event to the event bus. */
public void post(Object event) {
PostingThreadState postingState = currentPostingThreadState.get();
List
EventBus会通过ThreadLocal为每个线程维护一个PostingThreadState对象,里面包含分发队列eventQueue、是否主线程isMainThread、是否分发中isPosting、是否取消canceled、当前正在分发的事件和Subscription。
post方法这里的逻辑比较简单,将事件加到队列中,如果此时未在分发中时,就调用postSingleEvent逐一分发。
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class> eventClass = event.getClass();
boolean subscriptionFound = false;
if (eventInheritance) {
List> 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));
}
}
}
如果eventInheritance为true的话,表示事件支持继承,会调用lookupAllEventTypes获得需要分发的事件列表(包含自身、父类与接口)。然后使用postSingleEventForEventType分发事件。如果找不到相应的订阅者的话,根据sendNoSubscriberEvent与logNoSubscriberMessages决定是否发送NoSubscriberEvent事件与打印日志。
继续跟踪postSingleEventForEventType:
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class> eventClass) {
CopyOnWriteArrayList 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;
}
通过在注册订阅者时得到的subscriptionsByEventType获取到这个事件的订阅者及订阅方法,依次调用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);
}
}
这里涉及到EventBus的四种ThreadMode:
- POSTING:在发布事件的线程执行。
- MAIN:在主线程执行。
- BACKGROUND:如果发布事件的线程为主线程则新建一个线程执行,否则在发布事件的线程执行。
- ASYNC:在新的线程执行。
理解了这四种模式,就不难理解背后的原理。POSTING无需多说,invokeSubscriber方法内利用Subscription里面的SubscriberMethod,反射调用订阅事件的方法。其实我们也能猜到,切换到主线程是用的Handler,而切换到新线程则使用线程池。
在将这三种ThreadMode之前,我们需要了解PendingPost这个类。PendingPost作为PendingPostQueue(PendingPost的队列)的一个元素,代表了一个待发送的事件。
final class PendingPost {
private final static List pendingPostPool = new ArrayList();
Object event;
Subscription subscription;
PendingPost next;
private PendingPost(Object event, Subscription subscription) {
this.event = event;
this.subscription = subscription;
}
static PendingPost obtainPendingPost(Subscription subscription, Object event) {
synchronized (pendingPostPool) {
int size = pendingPostPool.size();
if (size > 0) {
PendingPost pendingPost = pendingPostPool.remove(size - 1);
pendingPost.event = event;
pendingPost.subscription = subscription;
pendingPost.next = null;
return pendingPost;
}
}
return new PendingPost(event, subscription);
}
static void releasePendingPost(PendingPost pendingPost) {
pendingPost.event = null;
pendingPost.subscription = null;
pendingPost.next = null;
synchronized (pendingPostPool) {
// Don't let the pool grow indefinitely
if (pendingPostPool.size() < 10000) {
pendingPostPool.add(pendingPost);
}
}
}
}
PendingPost里面持有事件event、订阅者信息subscription以及队列的下个节点next。仔细看下PendingPost里面的static变量(List)以及static方法(obtainPendingPost和releasePendingPost),其实跟前面分析的FindState一样都利用了对象池的思想避免的对象的频繁创建。
ThreadMode.MAIN:
当ThreadMode是Main时,如果当前是主线程,直接利用invokeSubscriber去处理;如果当前不是主线程的话,则调用HandlerPoster.enqueue去处理。
final class HandlerPoster extends Handler {
private final PendingPostQueue queue;
private final int maxMillisInsideHandleMessage;
private final EventBus eventBus;
private boolean handlerActive;
// ......
void enqueue(Subscription subscription, Object event) {
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
synchronized (this) {
queue.enqueue(pendingPost);
if (!handlerActive) {
handlerActive = true;
if (!sendMessage(obtainMessage())) {
throw new EventBusException("Could not send handler message");
}
}
}
}
@Override
public void handleMessage(Message msg) {
boolean rescheduled = false;
try {
long started = SystemClock.uptimeMillis();
while (true) {
PendingPost pendingPost = queue.poll();
if (pendingPost == null) {
synchronized (this) {
// Check again, this time in synchronized
pendingPost = queue.poll();
if (pendingPost == null) {
handlerActive = false;
return;
}
}
}
eventBus.invokeSubscriber(pendingPost);
long timeInMethod = SystemClock.uptimeMillis() - started;
if (timeInMethod >= maxMillisInsideHandleMessage) {
if (!sendMessage(obtainMessage())) {
throw new EventBusException("Could not send handler message");
}
rescheduled = true;
return;
}
}
} finally {
handlerActive = rescheduled;
}
}
}
enqueue方法内先利用PendingPost里的对象池获取PendingPost实例,enqueue到队列中。然后利用Handler将线程切换到主线程中。可以看到在handleMessage里面,最后也是交给invokeSubscriber去处理。
ThreadMode.BACKGROUND:
当ThreadMode是BACKGROUND时,如果当前不是主线程的话,直接交给invokeSubscriber去处理;否则,利用BackgroundPoster.enqueue切换到后台线程;
final class BackgroundPoster implements Runnable {
private final PendingPostQueue queue;
private final EventBus eventBus;
private volatile boolean executorRunning;
BackgroundPoster(EventBus eventBus) {
this.eventBus = eventBus;
queue = new PendingPostQueue();
}
public void enqueue(Subscription subscription, Object event) {
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
synchronized (this) {
queue.enqueue(pendingPost);
if (!executorRunning) {
executorRunning = true;
eventBus.getExecutorService().execute(this);
}
}
}
@Override
public void run() {
try {
try {
while (true) {
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;
}
}
}
eventBus.invokeSubscriber(pendingPost);
}
} catch (InterruptedException e) {
Log.w("Event", Thread.currentThread().getName() + " was interruppted", e);
}
} finally {
executorRunning = false;
}
}
}
这里的处理过程是类似的,先通过PendingPost对象池获取实例,添加到PendingPostQueue中。利用EventBus类里的线程池executorService切换到后台线程。在run方法内,从队列中取出PendingPost后,同样交给invokeSubscriber处理。
ThreadMode.ASYNC:
当ThreadMode是ASYNC时,直接AsyncPoster.enqueue切换到新线程。
class AsyncPoster implements Runnable {
private final PendingPostQueue queue;
private final EventBus eventBus;
AsyncPoster(EventBus eventBus) {
this.eventBus = eventBus;
queue = new PendingPostQueue();
}
public void enqueue(Subscription subscription, Object event) {
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
queue.enqueue(pendingPost);
eventBus.getExecutorService().execute(this);
}
@Override
public void run() {
PendingPost pendingPost = queue.poll();
if(pendingPost == null) {
throw new IllegalStateException("No pending post available");
}
eventBus.invokeSubscriber(pendingPost);
}
}
AsyncPoster跟BackGroundPoster的操作基本一致。
public synchronized void unregister(Object subscriber) {
List> 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());
}
}
注销订阅者比较简单,调用unsubscribeByEventType逐一取消订阅即可。
private void unsubscribeByEventType(Object subscriber, Class> eventType) {
List subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions != null) {
int size = subscriptions.size();
for (int i = 0; i < size; i++) {
Subscription subscription = subscriptions.get(i);
if (subscription.subscriber == subscriber) {
subscription.active = false;
subscriptions.remove(i);
i--;
size--;
}
}
}
}
获取订阅某个事件的Subscription列表,将active标示为false。
所谓粘性事件,指的是订阅者在事件发送之后才注册的也能接收到的事件。发送粘性事件是通过EventBus.getDefault().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);
}
除了像普通事件那样发送,还将事件记录到stickyEvents中。
既然粘性事件是在注册时能接收到之前发送的事件,看看注册时subscribe有关的逻辑:
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
// ……
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).
Set, Object>> entries = stickyEvents.entrySet();
for (Map.Entry, Object> entry : entries) {
Class> candidateEventType = entry.getKey();
if (eventType.isAssignableFrom(candidateEventType)) {
Object stickyEvent = entry.getValue();
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
} else {
Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
}
粘性事件,如果是继承的,对于事件自身和父类都调用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, Looper.getMainLooper() == Looper.myLooper());
}
}
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
// ……
}
checkPostStickyEventToSubscription方法内调用了postToSubscription,postToSubscription我们在上面分析过了,不做冗余。
可以看到粘性事件其实很简单,只是将事件缓存起来,等到注册订阅者时再做判断。
在注册订阅者时,如果配置了索引类,就通过索引类直接来获取SubscriberInfo,免去了低效的反射过程。这个索引类就是在编译时通过注解处理器生成的。在分析注解处理器前,先看看它的“杰作”长什么样子:
/** This class is generated by EventBus, do not edit. */
public class MyEventBusIndex implements SubscriberInfoIndex {
private static final Map, SubscriberInfo> SUBSCRIBER_INDEX;
static {
SUBSCRIBER_INDEX = new HashMap, SubscriberInfo>();
putIndex(new SimpleSubscriberInfo(MainActivity.class, true, new SubscriberMethodInfo[] {
new SubscriberMethodInfo("onMessageEvent", MessageEvent.class, ThreadMode.MAIN),
}));
}
private static void putIndex(SubscriberInfo info) {
SUBSCRIBER_INDEX.put(info.getSubscriberClass(), info);
}
@Override
public SubscriberInfo getSubscriberInfo(Class> subscriberClass) {
SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass);
if (info != null) {
return info;
} else {
return null;
}
}
}
可以看到MyEventBusIndex存储了以订阅者Class为key,SubscriberInfo为value的Map。
@Override
public boolean process(Set extends TypeElement> annotations, RoundEnvironment env) {
Messager messager = processingEnv.getMessager();
try {
String index = processingEnv.getOptions().get(OPTION_EVENT_BUS_INDEX);
if (index == null) {
messager.printMessage(Diagnostic.Kind.ERROR, "No option " + OPTION_EVENT_BUS_INDEX +
" passed to annotation processor");
return false;
}
// ……
int lastPeriod = index.lastIndexOf('.');
String indexPackage = lastPeriod != -1 ? index.substring(0, lastPeriod) : null;
// ……
collectSubscribers(annotations, env, messager);
checkForSubscribersToSkip(messager, indexPackage);
if (!methodsByClass.isEmpty()) {
createInfoIndexFile(index);
} else {
messager.printMessage(Diagnostic.Kind.WARNING, "No @Subscribe annotations found");
}
writerRoundDone = true;
} catch (RuntimeException e) {
// IntelliJ does not handle exceptions nicely, so log and print a message
e.printStackTrace();
messager.printMessage(Diagnostic.Kind.ERROR, "Unexpected error in EventBusAnnotationProcessor: " + e);
}
return true;
}
在process里首先获取配置在grade里面的eventBusIndex参数,即生成类的完整路径。如果获取不到,则结束。然后collectSubscribers获取所有订阅者的信息,checkForSubscribersToSkip筛选掉不符合的订阅者。最后利用createInfoIndexFile生成索引类。
collectSubscribers:
private void collectSubscribers(Set extends TypeElement> annotations, RoundEnvironment env, Messager messager) {
for (TypeElement annotation : annotations) {
Set extends Element> elements = env.getElementsAnnotatedWith(annotation);
for (Element element : elements) {
if (element instanceof ExecutableElement) {
ExecutableElement method = (ExecutableElement) element;
if (checkHasNoErrors(method, messager)) {
TypeElement classElement = (TypeElement) method.getEnclosingElement();
methodsByClass.putElement(classElement, method);
}
} else {
messager.printMessage(Diagnostic.Kind.ERROR, "@Subscribe is only valid for methods", element);
}
}
}
}
遍历需要处理的注解集合annotations,通过env.getElementsAnnotatedWith获取包含这个注解的元素。如果注解是修饰在ExecutableElement(方法)上的话,先通过checkHasNoErrors检验修饰符不为static、为public、只包含一个参数,然后加入到methodsByClass内。
checkForSubscribersToSkip:
/**
* Subscriber classes should be skipped if their class or any involved event class are not visible to the index.
*/
private void checkForSubscribersToSkip(Messager messager, String myPackage) {
for (TypeElement skipCandidate : methodsByClass.keySet()) {
TypeElement subscriberClass = skipCandidate;
while (subscriberClass != null) {
if (!isVisible(myPackage, subscriberClass)) {
boolean added = classesToSkip.add(skipCandidate);
// ……
break;
}
List methods = methodsByClass.get(subscriberClass);
if (methods != null) {
for (ExecutableElement method : methods) {
String skipReason = null;
VariableElement param = method.getParameters().get(0);
TypeMirror typeMirror = getParamTypeMirror(param, messager);
if (!(typeMirror instanceof DeclaredType) ||
!(((DeclaredType) typeMirror).asElement() instanceof TypeElement)) {
skipReason = "event type cannot be processed";
}
if (skipReason == null) {
TypeElement eventTypeElement = (TypeElement) ((DeclaredType) typeMirror).asElement();
if (!isVisible(myPackage, eventTypeElement)) {
skipReason = "event type is not public";
}
}
if (skipReason != null) {
boolean added = classesToSkip.add(skipCandidate);
// ……
}
}
}
subscriberClass = getSuperclass(subscriberClass);
}
}
}
checkForSubscribersToSkip里校验了:
① 检查订阅者类与事件类是不是public或者与索引类在同一包路径下,即可访问的。
② 参数需要为对象与接口,不可以是int、double等基础类型,但是可以对应的包装类。
对于那些checkForSubscribersToSkip不通过的类,也能在运行时通过反射来工作,具体可以见上面的findUsingInfo方法的分析。
createInfoIndexFile:
createInfoIndexFile通过IO方法来生成索引类,比较容易理解,这里就不分析了。
一路分析下来,我们发现EventBus其实逻辑不复杂。
通过在注册Subscriber时,先获取Subscriber内所有的SubscriberMethod,然后将订阅者Subscriber及其订阅的事件、订阅事件及其订阅者信息Subscription分别关联起来起来,使得在发送事件时能通过反射调用订阅方法。而获取SubscriberMethod的过程,要么是通过findUsingInfo在索引类里直接获取,要么是通过findUsingReflection反射获取带有@Subscribe的方法,前者的效率大大优于后者。而粘性事件的原理也很简单,在发送时将事件保存起来,而在注册时检查是不是有对应的粘性事件。
不仅如此,我们可以发现EventBus内为了追求效率做了很多优化,例如利用对象池避免对象的频繁创建、利用缓存变量避免多次调用反射等。
以上!