简介
众所周知,EventBus 是一款用在 Android 开发中的发布 / 订阅事件总线框架,基于观察者模式,将事件的接收者和发送者分开,简化了组件之间的通信操作,使用简单、效率高、体积小!
EventBus 使用了典型的发布 / 订阅事件模式,下面是 EventBus 官方给出的原理示意图。
安装依赖
使用 EventBus 之前,需要先添加 EventBus 依赖,EventBus 支持 gradle 和 maven 两种方式依赖,gradle 依赖的脚本如下:
implementation 'org.greenrobot:eventbus:3.1.1'
maven 依赖的配置如下:
<dependency>
<groupId>org.greenrobot</groupId>
<artifactId>eventbus</artifactId>
<version>3.1.1</version>
</dependency>
基本使用
EventBus 的使用步骤分为定义事件、订阅事件、发送事件、处理事件、取消订阅五步。
1,首先,定义一个事件类,里面添加需要发送的数据内容,如下:
public static class MessageEvent {/* Additional fields if needed */}
2,然后,在需要接收事件的地方订阅事件,可以选择注册事件订阅方法。
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {/* Do something */};
为了不造成资源的浪费或其他问题,需要在 onStart 函数中注册订阅事件,然后再 onStop 函数中取消订阅事件。
@Override
public void onStart() {super.onStart();
EventBus.getDefault().register(this);
}
@Override
public void onStop() {super.onStop();
EventBus.getDefault().unregister(this);
}
3,最后,将处理完成的数据发送出去。
EventBus.getDefault().post(new MessageEvent());
EventBus 原理剖析
要理解 EventBus 背后的原理,可以从以下几个方面着手:
- Subscribe 注解
- 注册事件订阅方法
- 取消注册
- 发送事件
- 事件处理
- 粘性事件
- Subscriber Index
- 流程梳理
Subscribe 注解
EventBus 从 3.0 开始使用 Subscribe 注解配置事件订阅方法,不再使用方法名,例如:
@Subscribe
public void handleEvent(String event) {// do something}
其中,事件类型可以是 Java 中已有的类型或者自定义的类型。下面是 Subscribe 注解的具体实现:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {
// 指定事件订阅方法的线程模式,即在那个线程执行事件订阅方法处理事件,默认为 POSTING
ThreadMode threadMode() default ThreadMode.POSTING;
// 是否支持粘性事件,默认为 false
boolean sticky() default false;
// 指定事件订阅方法的优先级,默认为 0,如果多个事件订阅方法可以接收相同事件的,则优先级高的先接收到事件
int priority() default 0;}
可以发现,在使用 Subscribe 注解时可以根据需求指定 threadMode、sticky、priority 三个属性。其中,threadMode 属性有如下几个可选值:
- ThreadMode.POSTING,默认的线程模式,在那个线程发送事件就在对应线程处理事件,避免了线程切换,效率高。
- ThreadMode.MAIN,如在主线程(UI 线程)发送事件,则直接在主线程处理事件;如果在子线程发送事件,则先将事件入队列,然后通过 Handler 切换到主线程,依次处理事件。
- ThreadMode.MAIN_ORDERED,无论在那个线程发送事件,都先将事件入队列,然后通过 Handler 切换到主线程,依次处理事件。
- ThreadMode.BACKGROUND,如果在主线程发送事件,则先将事件入队列,然后通过线程池依次处理事件;如果在子线程发送事件,则直接在发送事件的线程处理事件。
- ThreadMode.ASYNC,无论在那个线程发送事件,都将事件入队列,然后通过线程池处理。
注册事件订阅方法
使用 EventBus 时,需要在在需要接收事件的地方订阅事件,注册事件的方式如下:
EventBus.getDefault().register(this);
点击打开 getDefault() 会发现,getDefault() 是一个单例方法,保证当前只有一个 EventBus 实例。
public static EventBus getDefault() {if (defaultInstance == null) {synchronized (EventBus.class) {if (defaultInstance == null) {defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
getDefault() 最终调用了 new EventBus(),如下:
public EventBus() {this(DEFAULT_BUILDER);
}
然后,EventBus 调用它的另一个构造函数来完成它相关属性的初始化:
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;
}
其中,DEFAULT_BUILDER 就是一个默认的 EventBusBuilder,如下:
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
如果有需要的话,我们也可以通过配置 EventBusBuilder 来更改 EventBus 的属性,例如:
EventBus.builder()
.eventInheritance(false)
.logSubscriberExceptions(false)
.build()
.register(this);
有了 EventBus 的实例,接下来就可以进行注册了。
public void register(Object subscriber) {
// 得到当前要注册类的 Class 对象
Class<?> subscriberClass = subscriber.getClass();
// 根据 Class 查找当前类中订阅了事件的方法集合,即使用了 Subscribe 注解、有 public 修饰符、一个参数的方法
// SubscriberMethod 类主要封装了符合条件方法的相关信息:// Method 对象、线程模式、事件类型、优先级、是否是粘性事等
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
// 循环遍历订阅了事件的方法集合,以完成注册
for (SubscriberMethod subscriberMethod : subscriberMethods) {subscribe(subscriber, subscriberMethod);
}
}
}
可以看到,register() 方法主要分为查找和注册两部分,首先来看查找的过程,主要是 findSubscriberMethods() 方法:
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
// METHOD_CACHE 是一个 ConcurrentHashMap,直接保存了 subscriberClass 和对应 SubscriberMethod 的集合,以提高注册效率,赋值重复查找。List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {return subscriberMethods;}
// 由于使用了默认的 EventBusBuilder,则 ignoreGeneratedIndex 属性默认为 false,即是否忽略注解生成器
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() 流程很清晰,即先从缓存中查找,如果找到则直接返回,否则去做下一步的查找过程,然后缓存查找到的集合,根据上边的注释可知 findUsingInfo() 方法会被调用。
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
// 初始状态下 findState.clazz 就是 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.clazz 为 subscriberClass 的父类 Class,即需要遍历父类
findState.moveToSuperclass();}
// 查找到的方法保存在了 FindState 实例的 subscriberMethods 集合中。// 使用 subscriberMethods 构建一个新的 List<SubscriberMethod>
// 释放掉 findState
return getMethodsAndRelease(findState);
}
findUsingInfo() 方法会在当前要注册的类以及其父类中查找订阅事件的方法,这里出现了一个 FindState 类,它是 SubscriberMethodFinder 的内部类,用来辅助查找订阅事件的方法,具体的查找过程在 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 类型,但非 abstract、static 等
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
// 获得当前方法所有参数的类型
Class<?>[] parameterTypes = method.getParameterTypes();
// 如果当前方法只有一个参数
if (parameterTypes.length == 1) {Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
// 如果当前方法使用了 Subscribe 注解
if (subscribeAnnotation != null) {
// 得到该参数的类型
Class<?> eventType = parameterTypes[0];
// checkAdd() 方法用来判断 FindState 的 anyMethodByEventType map 是否已经添加过以当前 eventType 为 key 的键值对,没添加过则返回 true
if (findState.checkAdd(method, eventType)) {
// 得到 Subscribe 注解的 threadMode 属性值,即线程模式
ThreadMode threadMode = subscribeAnnotation.threadMode();
// 创建一个 SubscriberMethod 对象,并添加到 subscriberMethods 集合
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");
}
}
}
到此 register() 方法中 findSubscriberMethods() 流程就分析完了,我们已经找到了当前注册类及其父类中订阅事件的方法的集合。
接下来,我们分析下具体的注册流程,即 register() 中的 subscribe() 方法。首先,我们看一下 subscribe() 方法的源码:
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
// 得到当前订阅了事件的方法的参数类型
Class<?> eventType = subscriberMethod.eventType;
// Subscription 类保存了要注册的类对象以及当前的 subscriberMethod
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
// subscriptionsByEventType 是一个 HashMap,保存了以 eventType 为 key,Subscription 对象集合为 value 的键值对
// 先查找 subscriptionsByEventType 是否存在以当前 eventType 为 key 的值
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
// 如果不存在,则创建一个 subscriptions,并保存到 subscriptionsByEventType
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);
}
}
// 添加上边创建的 newSubscription 对象到 subscriptions 中
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;
}
}
// typesBySubscribere 也是一个 HashMap,保存了以当前要注册类的对象为 key,注册类中订阅事件的方法的参数类型的集合为 value 的键值对
// 查找是否存在对应的参数类型集合
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
// 不存在则创建一个 subscribedEvents,并保存到 typesBySubscriber
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 两个 HashMap。其中,发送事件的时候要用到 subscriptionsByEventType,完成事件的处理。当取消 EventBus 注册的时候要用到 typesBySubscriber、subscriptionsByEventType,完成相关资源的释放。
取消注册
接下来,我们看一下 EventBus 取消事件注册的流程。
EventBus.getDefault().unregister(this);
其中,unregister() 的源码如下:
public synchronized void unregister(Object subscriber) {
// 得到当前注册类对象 对应的 订阅事件方法的参数类型 的集合
List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
if (subscribedTypes != null) {
// 遍历参数类型集合,释放之前缓存的当前类中的 Subscription
for (Class<?> eventType : subscribedTypes) {unsubscribeByEventType(subscriber, eventType);
}
// 删除以 subscriber 为 key 的键值对
typesBySubscriber.remove(subscriber);
} else {logger.log(Level.WARNING, "Subscriber to unregister was not registered before:" + subscriber.getClass());
}
}
unregister() 方法调用了 unsubscribeByEventType() 方法:
private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
// 得到当前参数类型对应的 Subscription 集合
List<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions != null) {int size = subscriptions.size();
// 遍历 Subscription 集合
for (int i = 0; i < size; i++) {Subscription subscription = subscriptions.get(i);
// 如果当前 subscription 对象对应的注册类对象 和 要取消注册的注册类对象相同,则删除当前 subscription 对象
if (subscription.subscriber == subscriber) {
subscription.active = false;
subscriptions.remove(i);
i--;
size--;
}
}
}
}
所以,在 unregister() 方法中,最主要的就是释放 typesBySubscriber、subscriptionsByEventType 中缓存的资源。
发送事件
在 EventBus 中,我们发送一个事件使用的是如下的方式:
EventBus.getDefault().post("Hello World!")
可以看到,发送事件就是通过 post() 方法完成的,post() 方法的源码如下:
public void post(Object event) {
// currentPostingThreadState 是一个 PostingThreadState 类型的 ThreadLocal
// PostingThreadState 类保存了事件队列和线程模式等信息
PostingThreadState postingState = currentPostingThreadState.get();
List<Object> eventQueue = postingState.eventQueue;
// 将要发送的事件添加到事件队列
eventQueue.add(event);
// isPosting 默认为 false
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()) {
// 发送单个事件
// eventQueue.remove(0),从事件队列移除事件
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
可以发现,post() 方法先将发送的事件保存到 List 的事件队列,然后通过循环出队列,将事件交给 postSingleEvent() 方法处理。
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
// eventInheritance 默认为 true,表示是否向上查找事件的父类
if (eventInheritance) {
// 查找当前事件类型的 Class,连同当前事件类型的 Class 保存到集合
List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
int countTypes = eventTypes.size();
// 遍历 Class 集合,继续处理事件
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() 方法中,根据 eventInheritance 属性,决定是否向上遍历事件的父类型,然后用 postSingleEventForEventType() 方法进一步处理事件。
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
// 获取事件类型对应的 Subscription 集合
subscriptions = subscriptionsByEventType.get(eventClass);
}
// 如果已订阅了对应类型的事件
if (subscriptions != null && !subscriptions.isEmpty()) {for (Subscription subscription : subscriptions) {
// 记录事件
postingState.event = event;
// 记录对应的 subscription
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;
}
可以发现,postSingleEventForEventType() 方法核心就是遍历发送的事件类型对应的 Subscription 集合,然后调用 postToSubscription() 方法处理事件。
处理事件
接着上面的 postToSubscription() 方法,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 {
// 如果是在子线程发送事件,则将事件入队列,通过 Handler 切换到主线程执行处理事件
// mainThreadPoster 不为空
mainThreadPoster.enqueue(subscription, event);
}
break;
// 无论在那个线程发送事件,都先将事件入队列,然后通过 Handler 切换到主线程,依次处理事件。// mainThreadPoster 不为空
case MAIN_ORDERED:
if (mainThreadPoster != null) {mainThreadPoster.enqueue(subscription, event);
} else {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);
}
}
可以看到,postToSubscription() 方法就是根据订阅事件方法的线程模式、以及发送事件的线程来判断如何处理事件,至于处理方式主要有两种:一种是在相应线程直接通过 invokeSubscriber() 方法,用反射来执行订阅事件的方法,这样发送出去的事件就被订阅者接收并做相应处理了。
首先,我们来看一下 invokeSubscriber() 方法:
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);
}
}
如果在子线程发送事件,则直接在发送事件的线程通过反射处理事件。
另外一种是先将事件入队列(其实底层是一个 List),然后做进一步处理,我们以 mainThreadPoster.enqueue(subscription, event) 为例简单的分析下,其中 mainThreadPoster 是 HandlerPoster 类的一个实例。
public class HandlerPoster extends Handler implements Poster {
private final PendingPostQueue queue;
private boolean handlerActive;
......
public void enqueue(Subscription subscription, Object event) {
// 用 subscription 和 event 封装一个 PendingPost 对象
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
synchronized (this) {
// 入队列
queue.enqueue(pendingPost);
if (!handlerActive) {
handlerActive = true;
// 发送开始处理事件的消息,handleMessage() 方法将被执行,完成从子线程到主线程的切换
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();
......
// 进一步处理 pendingPost
eventBus.invokeSubscriber(pendingPost);
......
}
} finally {handlerActive = rescheduled;}
}
}
可以发现,HandlerPoster 的 enqueue() 方法主要就是将 subscription、event 对象封装成一个 PendingPost 对象,然后保存到队列里,之后通过 Handler 切换到主线程,在 handleMessage() 方法将中将 PendingPost 对象循环出队列,交给 invokeSubscriber() 方法做进一步处理。
void invokeSubscriber(PendingPost pendingPost) {
Object event = pendingPost.event;
Subscription subscription = pendingPost.subscription;
// 释放 pendingPost 引用的资源
PendingPost.releasePendingPost(pendingPost);
if (subscription.active) {
// 用反射来执行订阅事件的方法
invokeSubscriber(subscription, event);
}
}
invokeSubscriber 方法比较简单,主要就是从 pendingPost 中取出之前保存的 event、subscription,然后用反射来执行订阅事件的方法,又回到了第一种处理方式。所以 mainThreadPoster.enqueue(subscription, event) 的核心就是先将将事件入队列,然后通过 Handler 从子线程切换到主线程中去处理事件。
backgroundPoster.enqueue() 和 asyncPoster.enqueue 也类似,内部都是先将事件入队列,然后再出队列,但是会通过线程池去进一步处理事件。
粘性事件
一般情况,我们使用 EventBus 都是准备好订阅事件的方法,然后注册事件,最后在发送事件,即要先有事件的接收者。但粘性事件却恰恰相反,我们可以先发送事件,后续再准备订阅事件的方法、注册事件。
首先,我们看一下 EventBus 的粘性事件是如何使用的:
EventBus.getDefault().postSticky("Hello World!");
粘性事件使用了 postSticky() 方法,postSticky() 方法的源码如下:
public void postSticky(Object event) {synchronized (stickyEvents) {stickyEvents.put(event.getClass(), event);
}
post(event);
}
postSticky() 方法主要做了两件事:先将事件类型和对应事件保存到 stickyEvents 中,方便后续使用;然后执行 post(event) 继续发送事件,这个 post() 方法就是之前发送的 post() 方法。所以,如果在发送粘性事件前,已经有了对应类型事件的订阅者,及时它是非粘性的,依然可以接收到发送出的粘性事件。
发送完粘性事件后,再准备订阅粘性事件的方法,并完成注册。核心的注册事件流程还是我们之前的 register() 方法中的 subscribe() 方法,前边分析 subscribe() 方法时,有一段没有分析的代码,就是用来处理粘性事件的。
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
......
......
......
// 如果当前订阅事件的方法的 Subscribe 注解的 sticky 属性为 true,即该方法可接受粘性事件
if (subscriberMethod.sticky) {
// 默认为 true,表示是否向上查找事件的父类
if (eventInheritance) {
// stickyEvents 就是发送粘性事件时,保存了事件类型和对应事件
Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
for (Map.Entry<Class<?>, Object> entry : entries) {Class<?> candidateEventType = entry.getKey();
// 如果 candidateEventType 是 eventType 的子类或
if (eventType.isAssignableFrom(candidateEventType)) {
// 获得对应的事件
Object stickyEvent = entry.getValue();
// 处理粘性事件
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
} else {Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
}
可以看到,处理粘性事件就是在 EventBus 注册时,遍历 stickyEvents,如果当前要注册的事件订阅方法是粘性的,并且该方法接收的事件类型和 stickyEvents 中某个事件类型相同或者是其父类,则取出 stickyEvents 中对应事件类型的具体事件,做进一步处理。
subscribe() 方法最核心的就是 checkPostStickyEventToSubscription() 方法。
private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {if (stickyEvent != null) {postToSubscription(newSubscription, stickyEvent, isMainThread());
}
}
Subscriber 索引
回顾上面对 EventBus 注册事件流程的分析,EventBus 主要是在项目运行时通过反射来查找订事件的方法信息,如果项目中有大量的订阅事件的方法,必然会对项目运行时的性能产生影响。其实除了在项目运行时通过反射查找订阅事件的方法信息,EventBus 还提供了在项目编译时通过注解处理器查找订阅事件方法信息的方式,生成一个辅助的索引类来保存这些信息,这个索引类就是 Subscriber Index,和 ButterKnife 的原理是类似的。
所以,我们在添加 EventBus 依赖的时候通常是下面这样的:
dependencies {
compile 'org.greenrobot:eventbus:3.1.1'
// 引入注解处理器
annotationProcessor 'org.greenrobot:eventbus-annotation-processor:3.1.1'
}
然后在项目的 Application 中添加如下配置,可以生成一个默认的 EventBus 单例。
EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus();
其中,MyEventBusIndex() 方法的源码如下:
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("changeText", String.class),
}));
}
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;}
}
}
其中 SUBSCRIBER_INDEX 是一个 HashMap,保存了当前注册类的 Class 类型和其中事件订阅方法的信息。
接下来,我们再来分析下使用 Subscriber 索引时 EventBus 的注册流程。首先,创建一个 EventBusBuilder,然后通过 addIndex() 方法添加索引类的实例。
public EventBusBuilder addIndex(SubscriberInfoIndex index) {if (subscriberInfoIndexes == null) {subscriberInfoIndexes = new ArrayList<>();
}
subscriberInfoIndexes.add(index);
return this;
}
即把生成的索引类的实例保存在 subscriberInfoIndexes 集合中,然后用 installDefaultEventBus() 创建默认的 EventBus 实例。
public EventBus installDefaultEventBus() {synchronized (EventBus.class) {if (EventBus.defaultInstance != null) {
throw new EventBusException("Default instance already exists." +
"It may be only set once before it's used the first time to ensure consistent behavior.");
}
EventBus.defaultInstance = build();
return EventBus.defaultInstance;
}
}
即用当前 EventBusBuilder 对象创建一个 EventBus 实例,这样我们通过 EventBusBuilder 配置的 Subscriber Index 也就传递到了 EventBus 实例中,然后赋值给 EventBus 的 defaultInstance 成员变量。
所以在 Application 中生成了 EventBus 的默认单例,这样就保证了在项目其它地方执行 EventBus.getDefault() 就能得到唯一的 EventBus 实例!
由于我们现在使用了 Subscriber Index 所以不会通过 findUsingReflectionInSingleClass() 来反射解析订阅事件的方法。我们重点来看 getSubscriberInfo() 方法。
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) {
// 根据注册类的 Class 类查找 SubscriberInfo
SubscriberInfo info = index.getSubscriberInfo(findState.clazz);
if (info != null) {return info;}
}
}
return null;
}
subscriberInfoIndexes 就是在前边 addIndex() 方法中创建的,保存了项目中的索引类实例,即 MyEventBusIndex 的实例,继续看索引类的 getSubscriberInfo() 方法,来到了 MyEventBusIndex 类中。
@Override
public SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) {SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass);
if (info != null) {return info;} else {return null;}
}
即根据注册类的 Class 类型从 SUBSCRIBER_INDEX 查找对应的 SubscriberInfo,如果我们在注册类中定义了订阅事件的方法,则 info 不为空,进而上边 findUsingInfo() 方法中 findState.subscriberInfo != null 成立,到这里主要的内容就分析完了,其它的和之前的注册流程一样。
所以 Subscriber Index 的核心就是项目编译时使用注解处理器生成保存事件订阅方法信息的索引类,然后项目运行时将索引类实例设置到 EventBus 中,这样当注册 EventBus 时,从索引类取出当前注册类对应的事件订阅方法信息,以完成最终的注册,避免了运行时反射处理的过程,所以在性能上会有质的提高。项目中可以根据实际的需求决定是否使用 Subscriber Index。
完整流程
下面我们再来看一下 EventBus 的完成流程,可以用以下的几张图: