本篇文章将从EventBus的常用使用步骤去逐步分析它的源码和内部实现,其中会额外提到粘性事件、观察者模式和接口回调的区别以及EventBus线程调度相关知识。
下面就从以下的使用流程去逐步分析EventBus的源码。
初始化->注册->发送事件->解除注册
使用EventBus时我们一般通过调用getDefault来获取EventBus对象,EventBus的获取实例方法仍然是一个单例来实现,再继续看,发现是熟悉的双重锁,但是接下来的构造函数和我们通常写的会有不同,们一我般会把构造函数用private封闭,但是这里并没有修改修饰符,而是直接调用了它的有参构造函数,传入一个builder进行构造,接着就成功完成了EventBus的初始化。
这里之所以不修改修饰符,源码注释里也给了说明
Creates a new EventBus instance; each instance is a separate scope in which events are delivered.To use a central bus, consider {@link #getDefault()}.
//双重锁单例
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
//这里并没有对构造函数封闭,和一般的单例不同,它允许直接调用构造函数创建实例
//this调用它自己的有参构造函数,并把DEFAULT_BUILDER当参数传入
public EventBus() {
this(DEFAULT_BUILDER);
}
//默认builder
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
//使用建造者模式 创建不同的EventBus实例
EventBus(EventBusBuilder builder) {
logger = builder.getLogger();
...
}
首先使用反射方法获取到传入对象的类型,接着通过subscriberMethodFinder的查找方法去寻找该订阅者希望订阅的方法,接着锁住当前实例,不断去遍历刚才找出的方法,调用subscribe去进行订阅。
所以整个注册过程就是 找出想要订阅的方法->为当前对象类型订阅这些方法。
希望订阅的方法:我把如下这类似方法在当前阶段称为希望订阅的方法是因为在这时,这些方法并没有成功进行订阅,在执行EventBus中的订阅逻辑后才能将其称为订阅方法。
@Subscribe(threadMode = ThreadMode.MAIN)
public void XXX(MessageEvent messageEvent) {
...
}
注册逻辑:
public void register(Object subscriber) {
//首先获取到传入的对象的类型
Class> subscriberClass = subscriber.getClass();
//找出当前订阅者订阅了的所有方法并存入集合
List subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
//遍历刚找出的方法 依次进行订阅
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
"其中SubscriberMethod记录了以下几种信息"
包括线程模式、对象类型、优先级、粘性等
final Method method;
final ThreadMode threadMode;
final Class> eventType;
final int priority;
final boolean sticky;
简单了解了下它的注册方法,接着我们去看看它是怎样找出订阅方法的还有就是怎么进行订阅的,这就对应着findSubscriberMethods 和 subscribe 这两个方法。
//方法缓存(使用的是ConcurrentHashMap以键值对的形式存入Map,key为传入对象类型,value为方法list)
private static final Map, List> METHOD_CACHE = new ConcurrentHashMap<>();
List findSubscriberMethods(Class> subscriberClass) {
//首先直接从缓存中去获取传入对象类型的相关订阅方法
List subscriberMethods = METHOD_CACHE.get(subscriberClass);
//如果方法列表不为空直接返回
if (subscriberMethods != null) {
return subscriberMethods;
}
if (ignoreGeneratedIndex) {
subscriberMethods = findUsingReflection(subscriberClass);
} else {
subscriberMethods = findUsingInfo(subscriberClass);
}
//如果注册方法为空则抛出异常(说明没有用@Subscribe注解,或者没有订阅方法)
//如果有的话则将其放入METHOD_CACHE中
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;
}
}
这个查找订阅方法首先会从一个方法缓存中去获取当前传入类型的所有订阅方法,如果方法列表已经存在不为空则直接返回已有的方法列表,然后根据ignoreGeneratedIndex的值来判断用哪个方法去查找订阅方法,接着就将查找到的方法放入方法缓存list里面。
在上面findSubscriberMethods中其实是有两类找出subscriberMethods的方法,一类是从METHOD_CACHE中去查找Map当中是否已经存有注册方法,第二类就是通过反射用那两种方法去找,但实际上都会调用到findUsingReflectionInSingleClass去查找相关订阅方法。
首先看下findUsingReflection方法
private List findUsingReflection(Class> subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
//调用查找订阅方法的方法
findUsingReflectionInSingleClass(findState);
//依次获取该class的父类,向上遍历并且向上move的过程会跳过系统class
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}
void moveToSuperclass() {
if (skipSuperClasses) {
clazz = null;
} else {
clazz = clazz.getSuperclass();
String clazzName = clazz.getName();
/** Skip system classes, this just degrades performance. */
if (clazzName.startsWith("java.") || clazzName.startsWith("javax.") || clazzName.startsWith("android.")) {
clazz = null;
}
}
}
在上述findUsingReflection当中,我们能够看到非常关键的一点,那就是EventBus支持事件订阅的继承。
接着在findUsingReflectionInSingleClass这个方法当中,整个过程是通过反射去判断方法是否符合“要求”,包括作用域(public)、形参长度、是否有注解 ,在经过一系列的判断之后将所有满足条件的method存入subscriberMethods这个ArrayList当中。
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) {
"判断作用域(是否为public)"
//先获取该方法的修饰符如果为public且不为abstract、static才能通过筛选
int modifiers = method.getModifiers();
//按位与操作符相同为1不同为0
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) {
//取得Event事件,调用checkAdd进行判断
"下面的checkAdd源码"
Class> eventType = parameterTypes[0];
if (findState.checkAdd(method, eventType)) {
//获取到当前注解指定的线程模式(threadMode)
ThreadMode threadMode = subscribeAnnotation.threadMode();
//将这个订阅方法加入ArrayList中存储
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");
}
}
}
//这个检测方法主要由两级检测,第一级速度快,单纯的对event的类型进行检测
//第二级是对其签名的检测
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);
}
}
接着是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);
}
到现在,我们已经找出了该对象(如:activity)所有订阅方法(根据作用域、形参数量、注解等筛选出的),接着我们要做的就是将这些方法进行订阅(subscribe)。
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class> eventType = subscriberMethod.eventType;
//将所有的观察者和订阅方法封装成一个subscription对象
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();
//根据方法的优先级来让其加入到subscription列表当中
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);
"对sticky的event的处理"
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);
}
}
}
在subscribe方法中,我们看到了EventBus对粘性事件(Sticky = true)的处理,所以这里有必要提一下粘性事件相关概念。我们看其中的逻辑,都调用了checkPostStickyEventToSubscription方法,接着跟进这个方法,我们看到的是postToSubscription这个在发送过程中起关键作用的代码,事件发送(post)我们等下再分析,但是我们必须弄清楚为什么在注册过程当中,一般事件都是将其存入事件列表就行了,为什么粘性事件在注册阶段就进行了事件发送的操作。所以我们得首先了解什么是粘性事件,以及为什么需要此类事件。
粘性事件和一般的事件有所不同,普通的事件通常是先注册再发布,而粘性事件一般是先发布再订阅。那么为什么会需要用到粘性事件呢?设想一个例子,我们要从Activity A往Activity B发送事件,让B根据A的消息执行相关逻辑,那么在这个例子当中,我们的B就是一个订阅者,A就是发布者,但如果B这个订阅者在A发布之后还未启动,那它是收不到这个消息的,所以粘性事件的存在就解决了这个问题,反正我这个事件先发布在这里,你有空了就来订阅就行,所以粘性事件也是解决了这种常见的问题。
所以,我们在了解了粘性事件的作用和工作原理之后,我们再来看注册这里的代码就能够理解了,就对应着上面那句话“为什么粘性事件在注册阶段就进行了事件发送的操作”,我们看上面的代码对粘性事件的处理部分可以发现,它首先取出了所有的粘性事件,因为它不知道你要的是哪一个粘性事件,所以得全部取出来遍历去判断,如果判断成功之后呢,这里就进入到了普通事件到发布阶段要做的操作,所以也就能理解为什么粘性事件在注册阶段就完成了事件的发送。
在执行完了subscribe方法之后,我们就相应的完成了事件的订阅。
接着进入更为关键的事件发送(post)流程。
我们还是从使用去看,以下是最简单的post event的代码。
EventBus.getDefault().post(messageEvent);
在事件发送的这个过程当中
public void post(Object event) {
//PostingThreadState当中维护了线程相关参数
//currentPostingThreadState是一个Thread Local,这里对于Thread Local而言设置和读取都快很多
PostingThreadState postingState = currentPostingThreadState.get();
List
在取出当前线程相关参数之后,取得当前发送线程中的事件队列,把传入的事件加入到事件队列当中,如果还有未发送的事件,则每次移除事件队列中的第一个事件并进行发送,接着在发送完成之后重置标志位,使当前线程中的事件重新成为可发送状态。
下面来看下postSingleEvent方法
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class> eventClass = event.getClass();
boolean subscriptionFound = false;
if (eventInheritance) {
//这里获取了所有父类class的集合
List> eventTypes = lookupAllEventTypes(eventClass);
int countTypes = eventTypes.size();
//循环该event所有的父类,并依次加入post
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));
}
}
}
在上面的postSingleEvent方法中,主要是对当前传入的event的父类进行获取,如果有就遍历并找到其所有父类,再遍历将每个父类当做一个单独的事件进行传入并发送,如果没有就直接将其当前类型传入进行发送。所以接下来进入到了postSingleEventForEventType这个方法当中。
postSingleEventForEventType方法
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class> eventClass) {
CopyOnWriteArrayList subscriptions;
synchronized (this) {
//根据event的class类型从subscription中取出对应的封装好的订阅事件
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;
}
上面方法的逻辑主要是根据上一步操作传入的class的类型去查找之前已经订阅好并进行封装了的subscription,并为当前发送状态赋值,再调用postToSubscription对相应事件进行发送。
postToSubscription方法
//根据线程模型选择执行对应逻辑
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {
case POSTING:
//如果是POSTING则直接通过反射invoke去调用该对应的订阅方法
invokeSubscriber(subscription, event);
break;
case MAIN:
//如果是主线程也同样直接调用对应订阅方法,如果不是则调用mainThreadPoster的enqueue
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);
}
}
在这个方法当中终于看到了eventBus对线程模型的相关逻辑。
首先,如果是POSTING这种线程模型,因为处理事件和发送事件的线程都是在同一个线程当中的,所以并不需要做特殊处理,直接调用invokeSubscriber,这里面又通过反射去调用了对应的订阅方法。
接着在MAIN中,如果是主线程的话,同样直接调用相关订阅方法,那么如果不是在主线程当中呢,这里就涉及到了EventBus中对线程调度问题,所以要弄明白它是怎样实现线程调度的话就得先看下mainThreadPoster和它的enqueue方法做了什么。首先这个poster是一个接口,但是在它的实现当中有一个HandlerPoster是继承自Handler并且实现了Poster这个接口的,所以初步猜想EventBus的线程调度应该是借助了Handler来完成的,其中的enqueue方法中完成了消息的发送,自身的handleMessage完成对消息的处理。
handlerPoster源码
public 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;
}
}
其次是BACKGROUND这个线程模型,如果是在主线程则调用backgroundPoster的enqueue方法,如果不是则直接通过反射qui调用其方法,这个backgroundPoster实现了Runnable和Poster接口的一个类,它其中提供的enqueue方法,它把pendingPost交给了线程池去处理,被调用后会执行run的回调,run方法中又是通过反射去调用了订阅方法。
backgroundPoster源码
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 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) {
eventBus.getLogger().log(Level.WARNING, Thread.currentThread().getName() + " was interruppted", e);
}
} finally {
executorRunning = false;
}
}
最后是ASYNC这种线程模型,这个它和backgound的第二种是一样的,也是通过线程池去执行,最后通过反射去调用。
首先也是了解下解除注册的常用流程,这里是直接调用了EventBus的unregister方法完成解除工作,我们先大概想一下,解除注册无非就是把该清除的清除,该重置的重置,大体应该就是这样,那么接下来去看看EventBus内部具体是怎么去解除注册的。
if(EventBus.getDefault().isRegistered(this)) {
EventBus.getDefault().unregister(this);
}
在看解除注册的源码之前,我们先回顾下EventBus中的两个参数,方便我们理解下解除注册流程到底干了什么。
"subscriptionsByEventType"
private final Map, CopyOnWriteArrayList> subscriptionsByEventType;
Class> eventType = subscriberMethod.eventType;
subscriptionsByEventType = new HashMap<>();
subscriptionsByEventType.put(eventType, subscriptions);
"typesBySubscriber"
private final Map
第一个是subscriptionsByEventType,HashMap(key:订阅方法的事件类型;value:泛型为Subscription的一个list),其中Subscription中包含有订阅对象和订阅方法的信息,具体订阅方法有方法,线程模型,事件类型,优先级,是否粘性事件等字段信息。
第二个是typesBySubscriber,HashMap(key:订阅对象的class类型;value:泛型为各种事件类型的list)
unregister源码
/** Unregisters the given subscriber from all event classes. */
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 {
logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
在unregister当中,首先通过订阅对象的class类型取出对应的该class订阅了的各类事件类型,然后依次进入unsubscribeByEventType方法去。
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--;
}
}
}
}
这里根据事件类型从subscriptionsByEventType中获取到对应的subscription,接着就是去将subscriptions的值依次remove掉,就完成了解除注册。
整体而言,EventBus是一个基于观察者模式而构建的框架,这里提到观察者模式,稍微提一下个人觉得和接口回调的区别,在开始时我觉得都差不多,没什么差别,其实现在看来我觉得接口回调可以看做观察者模式的一个特例吧,也就是接口回调相当于是只有一个观察者的观察者模式,所以我认为区别就在于观察者模式是一个一对多的关系,所有观察者接收到消息后都能够自动更新。而且EventBus在观察者这个模式上又进行了上层的一个处理,EventBus将事件和事件处理类大幅解耦,让我们的使用和代码整洁度都有了比较好的一个提升。