前言:EventBus在现如今Android项目中用来进行通信使用很广泛。在本篇文章中,我将会分为下面几个模块来讲解EventBus:如何使用,源码分析,使用注意事项,高级技能。
- 在官网中有这么一句话来描述EventBus
Event bus for Android and Java that simplifies communication between Activities, Fragments, Threads, Services, etc. Less code, better quality
,意思就是说EventBus用来在Android和Java中简化Activities, Fragments,线程和服务之间的通信,具有代码量少,质量高的优点,另外包的体积很小,只有50KB大小,并且响应速度快。
1 如何使用:
-
首先声明一个Event class:
public class MessageEvent{}
-
声明并且注解订阅方法,可以通过注解选择指定的线程:
@Subscribe(threadMode = ThreadMode.MAIN) public void onMessageEvent(MessageEvent event) {/* Do something */};
-
注册和解除注册订阅者,对于Android的Activity, Fragment,一般应该依据生命周期来进行注册和解除注册:
@Override public void onStart() { super.onStart(); EventBus.getDefault().register(this); } @Override public void onStop() { super.onStop(); EventBus.getDefault().unregister(this); }
-
发送Event事件
EventBus.getDefault().post(new MessageEvent());
-
发送的时候就会执行上面
Do something中的内容
。可以把EventBus看作一个观察者模式来理解,不太了解观察者模式的可以点击 Java设计模式-观察者模式 查看。EventBus作为一个被观察者,通过register
持有观察者对象,而通过post方法就将事件传递到观察者中执行了。讲解完基本使用方法后我们主要来看一下源码部分,学习源码要避免深入细节不可自拔,只见树木不见森林,学习框架源码有很多好处,其中最主要的是有助于我们更好的使用框架,另外在看框架源码的过程中可以学习优秀框架的思想,EventBus源码其实也比较简单,下面来看一下。
2 源码部分
-
我们首先是要获取EventBus实例对象,通过
EventBus.getDefault()
,那么首先看看getDefault
方法,这是一个典型的双重锁定单例模式,不太了解观察者模式的可以点击设计模式——单例模式(创建型模式)查看,volatile
关键字具有禁止指令重排序的功能,我们还看到了有一个类EventBusBuilder
,而且而且EventBus中builder()
方法是public修饰的
,那么我们可以通过EventBus.builder().build()
来定制自己的EventBus实例,使用的是建造者模式,建造者模式可以点击建造者模式了解,区别是一个是加锁的,一个没加锁的,不过基本不这么用。public class EventBus { static volatile EventBus defaultInstance; /** Convenience singleton for apps using a process-wide EventBus instance. */ public static EventBus getDefault() { if (defaultInstance == null) { synchronized (EventBus.class) { if (defaultInstance == null) { defaultInstance = new EventBus(); } } } return defaultInstance; } public EventBus() { this(DEFAULT_BUILDER); } EventBus(EventBusBuilder builder) { logger = builder.getLogger(); subscriptionsByEventType = new HashMap<>(); typesBySubscriber = new HashMap<>(); stickyEvents = new ConcurrentHashMap<>(); mainThreadSupport = builder.getMainThreadSupport(); mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null; 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; } public static EventBusBuilder builder() { return new EventBusBuilder(); }
-
获取了实例之后就要执行注册
register()
方法了,我们来看一下这个方法:public void register(Object subscriber) { Class<?> subscriberClass = subscriber.getClass(); // 1 List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass); synchronized (this) { for (SubscriberMethod subscriberMethod : subscriberMethods) { subscribe(subscriber, subscriberMethod); } } } // 2 public class SubscriberMethod { final Method method; final ThreadMode threadMode; final Class<?> eventType; final int priority; final boolean sticky; /** Used for efficient comparison */ String methodString; 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; } // 3 private static final Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>(); List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) { List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass); if (subscriberMethods != null) { // 4 return subscriberMethods; } // 5 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; } }
-
首先获取注册对象的Class类型,然后在标注1处通过
subscriberMethodFinder
实例查找注册对象的订阅方法,订阅方法类在标注2处,其中持有method, threadMode , eventType, priority, sticky
,其实subscriberMethodFinder
是通过一个Map对象来操作的, 请看标注3处。 如果执行到标注4处,缓存中没有的话就会接着往下走,标注5处默认为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); }
-
这里使用了
FindState
,首先通过prepareFindState
来返回一个findState
实例,然后通过initForSubscriber
将注册类绑定到实例中,便于后期处理。这里有一个while
循环,我们因为一般情况下走else
逻辑,所以我们直接分析findUsingReflectionInSingleClass
逻辑,这个方法应该是最直接明了,简单易懂的了,我们来看看: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; } // 遍历方法 for (Method method : methods) { int modifiers = method.getModifiers(); // 方法的权限修饰符必须是public的,并且非static,非abstract的 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]; // 将符合条件的方法封装成SubscriberMethod方法放入findState的subscriberMethods列表中 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"); } } }
-
这里很明显是通过反射来使用的,首先通过方法
getDeclaredMethods
获取注册类中的方法,其中包括公共 保护 默认 私有的方法 但是不包括继承的方法以及接口中default的方法
, 如果抛出异常NoClassDefFoundError
的话就会使用getMethods
方法来获取注册类中的方法,其中包括public声明的方法包括 public声明的继承的方法,以及接口中的default的方法
。获取到方法列表之后,就要对方法进行遍历处理了,注解中写的比较清晰,就不详细说了,需要注意的是,这里有几个异常需要注意:1 如果你的订阅方法的修饰符不符合要求,会抛出
EventBusException
异常;
2 如果你的参数列表中个数不是一个的话,也会抛出EventBusException
异常。
3 如果你的注册类中没有@Subscriber
修饰的方法的话,也会抛出EventBusException
异常(这个异常是在方法findSubscriberMethods
中抛出的) -
然后我们回到
register
中看subscriber
方法:public void register(Object subscriber) { Class<?> subscriberClass = subscriber.getClass(); List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass); synchronized (this) { for (SubscriberMethod subscriberMethod : subscriberMethods) { subscribe(subscriber, subscriberMethod); } } } // Must be called in synchronized block private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType; private final Map<Object, List<Class<?>>> typesBySubscriber; private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) { Class<?> eventType = subscriberMethod.eventType; // 将观察者和订阅方法封装成一个对象,然后放入subscriptionsByEventType中 Subscription newSubscription = new Subscription(subscriber, subscriberMethod); // 从缓存中尝试获取事件类型对应的subscriptions列表 CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType); if (subscriptions == null) { subscriptions = new CopyOnWriteArrayList<>(); subscriptionsByEventType.put(eventType, subscriptions); } else { //如果subscriptions中已经包含了newSubscription对象,就会抛出异常,这里equals方法被重写了 if (subscriptions.contains(newSubscription)) { throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event " + eventType); } } int size = subscriptions.size(); // 这里会根据优先级顺序决定newSubscription在队列中的位置 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); // 如果黏性事件的话,按照如下流程处理 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); } } }
-
上述
subscribe
方法的主线也比较清晰,主要是使用了subscriptionsByEventType
和typesBySubscriber
来进行存储相关的信息。 -
unregister
方法就是将所有的订阅信息从缓存中清除掉,如下: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 { logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass()); } } 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--; } } } }
-
接下来我们看最后的
post
方法,public void post(Object event) { PostingThreadState postingState = currentPostingThreadState.get(); List<Object> eventQueue = postingState.eventQueue; eventQueue.add(event); if (!postingState.isPosting) { postingState.isMainThread = isMainThread(); 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; } } } final static class PostingThreadState { final List<Object> eventQueue = new ArrayList<>(); boolean isPosting; boolean isMainThread; Subscription subscription; Object event; boolean canceled; }
-
首先
currentPostingThreadState
是一个ThreadLocal对象,这里ThreadLocal能保证每一个线程都单独有一个currentPostingThreadState
对象,至于是怎么做到的,网上有很多讲解的文章,就不详细说了,简单来说就是因为每一个Thread都会持有ThreadLocal.ThreadLocalMap
对象,很明显这是一个类似于Map的对象,可以按照一个Map来理解,而这个map的key就是ThreadLocal本身,所以就会导致每一个线程都会持有自己独一份的对象,互不干涉其它线程。最终走到while
循环,不停的循环发送事件,直到队列中事件处理完毕。然后走进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) { logger.log(Level.FINE, "No subscribers registered for event " + eventClass); } if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class && eventClass != SubscriberExceptionEvent.class) { post(new NoSubscriberEvent(this, event)); } } }
-
eventInheritance
通过lookupAllEventTypes
到addInterfaces
可以看出,代表的是是否遍历当前事件的所有父类事件信息,默认是true
,我们进入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
,它存储的就是我们事件类型对应的Subscription
集合,其中包含的就是订阅方法信息和观察者,然后for循环遍历集合,执行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 MAIN_ORDERED: if (mainThreadPoster != null) { mainThreadPoster.enqueue(subscription, event); } else { // temporary: technically not correct as poster not decoupled from subscriber invokeSubscriber(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); } }
-
上面的方法逻辑就很明白了,就是根据
threadMode
来决定执行的逻辑,最终都执行到了invokeSubscriber
方法中, 其中mainThreadPoster
查看源码直到是一个Handler,在handleMessage
中执行到了方法invokeSubscriber(pendingPost)
方法(这是一个方法重载
),最终也是执行到了invokeSubscriber(Subscription subscription, Object event)
:void invokeSubscriber(PendingPost pendingPost) { Object event = pendingPost.event; Subscription subscription = pendingPost.subscription; PendingPost.releasePendingPost(pendingPost); if (subscription.active) { invokeSubscriber(subscription, event); } } 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); } }
-
我们能看到最终是使用反射调取到了方法,执行了我们真实的订阅方法的逻辑,至此源码分析结束。
3 注意事项
除了上面说的几个Exception,如果我们重复调用register
的话,会出现throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "+ eventType);
异常,但是如果重复unregister
倒是不影响,会有log提示Subscriber to unregister was not registered before xxx
.
4 高级技能
- 我们可以查看到有一个API是
hasSubscriberForEvent
,这个API可以用来判断是否注册了某一个event
事件, 这个的用处是很大的,比如应用升级,如果应用在前台的话,升级就会影响用户体验,这时候就可以使用这个,我们在用户将应用退出前台的时候post升级event
事件在升级类中后台执行升级逻辑,不影响用户体验。这个和在Activity中生命周期中处理是不同的,因为我们不可能将升级的逻辑放在Activity中处理,这时候我们有一个单独的类处理升级逻辑,就可以在Activity生命周期onStop
中post事件
,在升级类中处理,这个需要仔细的琢磨一下。 - 我们可以查看到有一个API是
isRegistered
, 这个是判断是否注册了某一个观察者,这个观察者不仅仅是Activity, 也可以是Java类等,注册之前判断一下,避免重复注册出现crash.
学习源码有助于我们更好的使用这个工具,并且从中学到很多好的思想,但是源码的学习首先要将脉络理清楚,避免深入细节不可自拔,文中有些细节读者可以自行深入研究,相信你会有很不错的收获的。