EventBus源码分析
来源:互联网 发布:贝尔.格里尔斯 知乎 编辑:程序博客网 时间:2024/06/07 04:57
相信大家都有在项目中使用过EventBus。EventBus是一个性能高效的基于观察者模式的事件发布与订阅框架。借助EventBus,我们只需几行代码便能实现组件之间、线程之间的通信,达到解耦的目的。
这篇文章不对EventBus的使用进行介绍,而是格物致知,探究EventBus的源码。
1. 入口
一般使用时是通过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来自定义配置。
2. 注册订阅者
接下来看下通过EventBus.getDefault().register()注册订阅者时发生了什么?
public void register(Object subscriber) { Class<?> subscriberClass = subscriber.getClass(); List<SubscriberMethod> 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<Subscription> 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<Class<?>> 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<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) { List<SubscriberMethod> 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
2.1. 索引获取SubscriberMethod
ignoreGeneratedIndex默认为false,因此会调用findUsingInfo:
private List<SubscriberMethod> 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内的临时变量,并放置到对象池中。
2.2. 反射获取SubscriberMethod
如果ignoreGeneratedIndex为true时,采用反射的方式获取SubscriberMethod。而且如果使用了EventBus3.0,却不配置使用索引类,这样其实最后都会“降级”为反射。
值得注意的是,如果采用EventBus3.0却不使用注解处理器的方式效率是比EventBus2.4还要低的。因为EventBus2.4是固定订阅方法名为onEvent+ThreadMode的形式,而3.0方法名是自定义的,需要添加@Subscribe注解,相比之下多了反射获取注解的时间。
private List<SubscriberMethod> 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的。
到这里注册订阅者的流程就结束了。
3. 发送事件
发送事件时,通过EventBus.getDefault.post(Event)来发送:
/** 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会通过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<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)); } }}
如果eventInheritance为true的话,表示事件支持继承,会调用lookupAllEventTypes获得需要分发的事件列表(包含自身、父类与接口)。然后使用postSingleEventForEventType分发事件。如果找不到相应的订阅者的话,根据sendNoSubscriberEvent与logNoSubscriberMessages决定是否发送NoSubscriberEvent事件与打印日志。
继续跟踪postSingleEventForEventType:
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;}
通过在注册订阅者时得到的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<PendingPost> pendingPostPool = new ArrayList<PendingPost>(); 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<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()); }}
注销订阅者比较简单,调用unsubscribeByEventType逐一取消订阅即可。
private void unsubscribeByEventType(Object subscriber, Class<?> eventType) { List<Subscription> 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<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(); 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<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX; static { SUBSCRIBER_INDEX = new HashMap<Class<?>, 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。
@Overridepublic 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<ExecutableElement> 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内为了追求效率做了很多优化,例如利用对象池避免对象的频繁创建、利用缓存变量避免多次调用反射等。
以上!
- EventBus 2.4 源码分析
- EventBus源码注释分析
- EventBus框架源码分析
- EventBus源码分析
- EventBus源码分析
- EventBus 源码分析
- EventBus 源码分析
- EventBus的源码分析
- EventBus 3 源码分析
- EventBus 源码分析
- EventBus的源码分析
- EventBus源码分析
- EventBus源码分析
- EventBus源码分析
- EventBus源码分析
- EventBus 3.0 源码分析
- EventBus 3.0 源码分析
- EventBus源码分析
- 【C#】数组合并
- dc_shell教程
- FastJsonUtil
- CryptoJS简单使用
- <9/24>集训周记
- EventBus源码分析
- 不务正业-二分基础
- 《Focusing Attention:Towards Accurate Text Recognition in Natural Images》阅读
- 【问题记录】使用fregment实现底部菜单,旋转屏幕时,fregment会增加
- 使用eclipse搭建ssh框架
- HTML学习笔记五 列表标签+表格标签
- 记录各个网站cookie的含义。
- matlab操作快捷键
- Android多点触控技术实战,自由地对图片进行缩放和移动