深入剖析 Android 开源库 EventBus 的源码详解

文章目录

  • 前言
  • 一、EventBus 简介
    • EventBus 三要素
    • EventBus 线程模型
  • 二、EventBus 使用
    • 1.添加依赖
    • 2.EventBus 基本使用
      • 2.1 定义事件类
      • 2.2 注册 EventBus
      • 2.3 EventBus 发起通知
  • 三、EventBus 源码详解
    • 1.Subscribe 注解
    • 2.注册事件订阅方法
      • 2.1 EventBus 实例
      • 2.2 EventBus 注册
        • 2.2.1 SubscriberMethodFinder#findSubscriberMethods()
          • 2.2.1.1 SubscriberMethodFinder#findUsingInfo()
          • 2.2.1.2 SubscriberMethodFinder#findUsingReflectionInSingleClass()
          • 2.2.1.3 SubscriberMethodFinder#FindState#checkAdd()
        • 2.2.2 EventBus#subscribe()
    • 3.EventBus 取消注册
      • 3.1 EventBus#unregister()
      • 3.2 EventBus#unsubscribeByEventType()
    • 4.EventBus 发布、处理事件
      • 4.1 EventBus#post()
      • 4.2 EventBus#postSingleEvent()
      • 4.3 EventBus#postSingleEventForEventType()
      • 4.4 EventBus#postToSubscription()
        • 4.4.1 EventBus#invokeSubscriber()
        • 4.4.2 HandlerPoster#enqueue()
        • 4.4.3 EventBus#invokeSubscriber()
        • 4.4.4 BackgroundPoster#enqueue()
        • 4.4.5 AsyncPoster#enqueue()
      • **EventBus** 发布事件(包括粘性事件)及处理流程
    • 5.EventBus 粘性事件
      • 5.1 EventBus#postSticky()
      • 5.2 EventBus#subscribe()
      • 5.3 EventBus#checkPostStickyEventToSubscription()
    • 6.EventBus 之 Subscriber Index
      • 6.1 EventBusIndex
      • 6.2 EventBusBuilder#addIndex()
      • 6.2 EventBusBuilder#installDefaultEventBus()
      • 6.3 SubscriberMethodFinder#findUsingInfo()
        • 6.3.1 SubscriberMethodFinder#getSubscriberInfo()
        • 6.3.2 SimpleSubscriberInfo#getSubscriberMethods()
      • 小结
  • 总结
  • 参考


前言

Android 项目开发的时候,经常会遇到组件与组件之间、组件与后台线程之间的通信, 比如:子线程中进行数据请求,请求数据成功后,通过 HandlerRxJava 等来通知 UI 主线程进行更新操作;两个 Fragment 之间可以通过 Listener 进行通信,简单的通信通过上述的技术手段也是可以满足需求的,但随着项目的不断迭代更新,程序越来越庞大时,就会要写很多的代码,从而导致代码严重的耦合问题。为了优化该问题,EventBus 事件总线应运而生。


一、EventBus 简介

EventBus 事件总线,是一款由 GreenRobot 开源的在 Android 开发中使用的发布/订阅事件总线框架,基于观察者模式,将事件的接收者和发送者分开解耦。用来替代广播 BroadCastReceiverstartActivityForResultHandler异步回调等来实现各组件间、组件与后台线程间的通信。

EventBus 优点:

  • 简化组件之间的通讯方式;
  • 对通信双方进行解藕;
  • 通过 ThreadMode 灵活切换工作线程;
  • 速度快、性能好、库比较小、不占内存;

首先看一下官方给出的 EventBus 原理图:
EventBus 原理图
Publisher 使用 post() 函数发出一个 Event 事件,SubscriberonEvent() 函数中接收事件、进行后续的处理。

EventBus 三要素

使用 EventBus 时的三个重要参与者:EventPublisherSubscriber

  • Event:事件,它可以是任意类型;
  • Publisher:事件的发布者,可以在任意线程里发布事件,一般情况下,使用 EventBus.getDefault() 方法就可以得到一个 EventBus 对象,然后再调用其 post(Object) 方法即可;
  • Subscriber:事件订阅者,在 EventBus3.0 之前我们必须定义以 onEvent 开头的那几个方法,分别是:onEventonEventMainThreadonEventBackgroundThreadonEventAsync,而在 3.0 之后事件处理的方法名可以随意取,不过需要加上注解 @subscribe(),并且要指定线程模型,默认是 POSTING

EventBus 线程模型

ThreadMode 线程模式,通过 threadMode 设置 onReceiveMsg() 方法将在哪个线程环境下被调用。其中 threadMode 属性有如下几个可选值:

  • ThreadMode.POSTING:默认的线程模式,订阅者的订阅方法将在发布事件的同一线程中被调用,避免了线程切换,效率高;但是可能发布事件的线程是主线程,所以需要避免在订阅方法中处理耗时操作;
  • ThreadMode.MAIN:订阅者的订阅方法将在 UI 线程被调用,如在 UI 主线程发布事件,则直接在主线程处理事件;如果在子线程发送事件,则先将事件入队列,然后通过 Handler 切换到主线程,依次处理事件;
  • ThreadMode.MAIN_ORDERED:无论在那个线程发送事件,都先将事件入队列,然后通过 Handler 切换到主线程,依次处理事件(注意:该模式下可以确保 post() 的调用是非阻塞的);
  • ThreadMode.BACKGROUND:表示订阅者的订阅方法在后台线程。如果发布事件的线程是 UI 主线程,那么将开启一个后台线程执行订阅方法;如果发布事件的线程是在后台线程,那么事件处理函数就使用该线程;
  • ThreadMode.ASYNC:表示无论发布事件的线程是哪一个,订阅者的订阅方法始终会新建一个子线程来执行。所以这种情况下可以做耗时操作,但是需要避免在同一时间进行大量的异步订阅,控制并发线程的数量。

说了这么多,最后再来看一下 EventBus 有何缺点:

  • 使用的时需定义很多 Event 类;
  • 需要自己注册和反注册,如果忘了反注册就会导致内存泄漏;
  • Event 在注册的时候会通过反射去遍历注册对象的方法,并在其中找出带有 @subscriber 标签的方法,性能不高(可以通过编译时解析注解,优化运行时反射带来的性能损耗)。

二、EventBus 使用

1.添加依赖

app 或底层 base 库中的 builde.gradle 文件中导入依赖库:

imlementation ‘org.greenrobot:eventbus:3.2.0

2.EventBus 基本使用

通过 EventBus 的三个重要参与者:EventSubscriberPublisher 来一步步学习 EventBus 的基本使用,

2.1 定义事件类

public class EventMessage<T> {private int code;	// 事件类型private T data;		// 数据public EventMessage(int code, T data){this.code=code;this.data=data;}public int getCode() {return code;}public void setCode(int code) {this.code = code;}public T getData() {return data;}public void setData(T data) {this.data = data;}@Overridepublic String toString() {return "EventMessage{" + "code=" + code + ", data=" + data + '}';}
}

2.2 注册 EventBus

public class EventBusActivity extends AppCompatActivity {@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);}@Overrideprotected void onStart() {super.onStart();// 注册 EventBusEventBus.getDefault().register(this);}// 接收事件、线程模式为 ThreadMode.POSTING@Subscribe(threadMode = ThreadMode.POSTING, priority = 1)public void onReceiveMsg(EventMessage message){Log.e("EventBus_Subscriber", "onReceiveMsg_POSTING: " + message.toString());}// 接收事件、线程模式为 ThreadMode.MAIN@Subscribe(threadMode = ThreadMode.MAIN, priority = 1)public void onReceiveMsg1(EventMessage message){Log.e("EventBus_Subscriber", "onReceiveMsg_MAIN: " + message.toString());}// 接收事件、线程模式为 ThreadMode.MAIN_ORDERED@Subscribe(threadMode = ThreadMode.MAIN_ORDERED, priority = 1)public void onReceiveMsg2(EventMessage message){Log.e("EventBus_Subscriber", "onReceiveMsg_MAIN_ORDERED: " + message.toString());}// 接收事件、线程模式为 ThreadMode.BACKGROUND@Subscribe(threadMode = ThreadMode.BACKGROUND, priority = 1)public void onReceiveMsg3(EventMessage message){Log.e("EventBus_Subscriber", "onReceiveMsg_BACKGROUND: " + message.toString());}// 接收事件、线程模式为 ThreadMode.ASYNC、同时设置 sticky 为 true,表示粘性事件@Subscribe(threadMode = ThreadMode.ASYNC, sticky = true, priority = 1)public void onReceiveMsg4(EventMessage message){Log.e("EventBus_Subscriber", "onReceiveMsg__ASYNC: " + message.toString());}@Overrideprotected void onDestroy() {super.onDestroy();// 解除注册的 EventBusEventBus.getDefault().unregister(this);}
}

2.3 EventBus 发起通知

通过 EventBus#post(eventMessage) 方法或者 EventBus#postSticky(eventMessage) 方法来发起事件:

@OnClick(R2.id.send_common_event)
public void clickCommon(){EventMessage message = new EventMessage(1, "发送普通事件");EventBus.getDefault().post(message);
}@OnClick(R2.id.send_sticky_event)
public void clickSticky(){EventMessage message = new EventMessage(1, "发送黏性事件");EventBus.getDefault().postSticky(message);
}

至此,通过 EventBus 的 post() 方法发起的事情,在 EventBusActivity 中就可以收到并做后续的处理。postSticky() 方法最终也是调用的 EventBus 的 post() 方法,后续通过分析源码来进行剖析其具体过程。


三、EventBus 源码详解

EventBus 的实现原理主要包括如下几个方面的内容:

  • Subscribe 注解
  • 注册事件订阅方法
  • 取消注册
  • 发布、处理事件
  • 粘性事件
  • Subscriber Index

1.Subscribe 注解

EventBus3.0 开始用 Subscribe 注解配置事件订阅方法,其共有三个参数(可选):threadModeboolean stickyint priority。 完整的写法如下:

@Subscribe(threadMode = ThreadMode.MAIN, sticky = true, priority = 1)
public void onReceiveMsg(EventMessage message) {Log.e(TAG, "onReceiveMsg: " + message.toString());
}
  • threadMode:用来设置 onReceiveMsg() 方法将在哪个线程环境下被调用,共有五种模式,参考上面的简介;
  • sticky:一个 Boolean 类型的变量,默认值为 false,即不开启黏性 sticky 特性。其作用是订阅者可以先不进行注册,如果 post() 事件已经发出,再注册订阅者,同样可以接收到事件,并进行处理;
  • priority:优先级,是一个 int 类型的变量,默认值为 0。值越大,优先级越高,越优先接收到事件。注意:只有在 post() 事件和事件接收处理处于同一个线程环境的时候,才有意义。

具体看下 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 注解时可以根据需求指定 threadModestickypriority 三个属性值。

2.注册事件订阅方法

EventBus 注册订阅事件流程
结合 EventBus 注册事件流程图,便于更好的理解源码执行流程,首先 EventBus 注册事件的方式如下:

EventBus.getDefault().register(this);

EventBus#getDefault() 是一个单例方法,保证当前只有一个 EventBus 实例对象:

2.1 EventBus 实例

public class EventBus {static volatile EventBus defaultInstance;// 默认的 EventBusBuilder 实例对象private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();// 通过 double check 双重校验获取 EventBus 单例对象public static EventBus getDefault() {if (defaultInstance == null) {synchronized (EventBus.class) {if (defaultInstance == null) {defaultInstance = new EventBus();}}}return defaultInstance;}// 创建一个新的EventBus实例;每个实例都是一个单独的作用域,事件在其中传递。// 要使用中央总线,请考虑{@link#getDefault()}public EventBus() {// 继续调用 EventBus 的另一个有参构造函数,传入默认的 EventBusBuilder 来完成它相关属性的初始化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;}
}

如果 defaultInstancenull,则新建一个 EventBus 实例对象赋值给 defaultInstance。最终通过调用 EventBus 的另一个有参构造函数,传入默认的 EventBusBuilder 来完成其相关属性的初始化。

可以通过 Builder 模式来支持用 EventBusBuilderEventBus 进行一些属性的配置,例如用如下方式注册事件:

EventBus.builder().eventInheritance(false).logSubscriberExceptions(false).build().register(this);

2.2 EventBus 注册

public class EventBus {private final SubscriberMethodFinder subscriberMethodFinder;// 注册订阅者 subscriber 以接收事件,订阅者一旦对接收事件不再感兴趣,就必须调用{@link #unregister(Object)}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);}}}
}

EventBus#register() 方法的执行流程如下:

  • 通过 SubscriberMethodFinder#findSubscriberMethods() 方法,根据当前要注册类的 Class 对象查找当前类中订阅了事件的方法集合 List,即找到使用了 Subscribe 注解、有 public 修饰符、一个参数的方法,其中 SubscriberMethod 类主要封装了符合条件方法的相关信息:Method 对象、线程模式、事件类型、优先级、是否是粘性事等;
  • 循环遍历订阅了事件的方法集合,通过 EventBus#subscribe() 方法完成注册。
2.2.1 SubscriberMethodFinder#findSubscriberMethods()
class SubscriberMethodFinder {// METHOD_CACHE是一个ConcurrentHashMap,保存了subscriberClass和对应SubscriberMethod的集合,以提高注册效率,避免重复查找private static final Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>();List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {// 获取 subscriberClass 类对应的 SubscriberMethod 的集合List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);if (subscriberMethods != null) { // 获取到且不为 null 则直接返回找到的 SubscriberMethod 的集合return subscriberMethods;}// ignoreGenereatedIndex 是用于标记是否忽略由 Builder 传入的 SubscriberInfoIndex// 由于使用了默认的 EventBusBuilder,则 ignoreGeneratedIndex 属性默认为 falseif (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;}}
}

SubscriberMethodFinder#findSubscriberMethods() 方法的执行流程如下:

  • 先从缓存集合 Map<Class<?>, List> METHOD_CACHE 中查找、获取 subscriberClass 类对应的 SubscriberMethod 的集合,如果找到则直接返回;
  • 如果查找不到,则根据条件判断进行下一步的查找过程,由于使用了默认的 EventBusBuilder,因此 ignoreGeneratedIndex 属性默认为 false,即忽略注解生成器,所以调用 SubscriberMethodFinder#findUsingInfo() 方法进行查找,最后将查找到的订阅事件的方法集合缓存到 METHOD_CACHE 中。
2.2.1.1 SubscriberMethodFinder#findUsingInfo()
class SubscriberMethodFinder {private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {// 通过 SubscriberMethodFinder#prepareFindState() 方法从 FindState 池中获取到非空的 FindState 并返回FindState findState = prepareFindState();findState.initForSubscriber(subscriberClass); // 初始化 FindState// 初始状态下 findState.clazz 就是 subscriberClass while (findState.clazz != null) {// 由于在 FindState.initForSubscriber() 方法初始化时 subscriberInfo 赋值为 null// 且没有通过 EventBusBuilder 向 List<SubscriberInfoIndex> subscriberInfoIndexes 集合中添加 SubscriberInfoIndexfindState.subscriberInfo = getSubscriberInfo(findState);if (findState.subscriberInfo != null) { // findState.subscriberInfo 为 nullSubscriberMethod[] 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 集合中// 使用 FindState.subscriberMethods 构建一个新的 List<SubscriberMethod>,然后释放掉 FindStatereturn getMethodsAndRelease(findState);}
}

通过注释可知在 SubscriberMethodFinder#findUsingInfo() 方法会在当前要注册的类以及其父类中查找订阅事件的方法,FindState 类是 SubscriberMethodFinder 的内部类,用来辅助查找订阅事件的方法,通过条件判断可知,接下来将通过 SubscriberMethodFinder#findUsingReflectionInSingleClass() 方法通过反射查找订阅事件的方法。

SubscriberMethodFinder 类中维护了一个 FindState 池,是一个默认大小为 4 的数组,通过 SubscriberMethodFinder#prepareFindState() 方法遍历该数组找到非 nullFindState 进行返回。而在 SubscriberMethodFinder#getMethodsAndRelease(findState) 方法中则是将搜寻的结果取出后,对 FindState 进行 recycle,之后再将其放回 FindState 池中。

2.2.1.2 SubscriberMethodFinder#findUsingReflectionInSingleClass()
class SubscriberMethodFinder {private void findUsingReflectionInSingleClass(FindState findState) {Method[] methods;try {// 获取订阅事件的 Method 列表,使用 getDeclaredMethods() 方法其实是比 getMethods() 方法的效率更高的// 尤其是对于较复杂庞大的类,如 Activity 类methods = findState.clazz.getDeclaredMethods();} catch (Throwable th) {// 但有时会导致 NoClassDefFoundError,此时采取备用方案,使用 getMethods() 进行获取methods = findState.clazz.getMethods();findState.skipSuperClasses = true;}// 循环遍历当前注册类的 methods 数组,筛选出符合条件的:public、non-static、non-abstract 的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) { // 检查其参数个数是否符合 1 的要求Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);if (subscribeAnnotation != null) { // 如果当前方法使用了 Subscribe 注解 Class<?> eventType = parameterTypes[0]; // 得到该参数的类型// FindState.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");}}}
}

SubscriberMethodFinder#findUsingReflectionInSingleClass() 方法的执行流程如下:

  1. 获取订阅事件的 Method 列表,注意:使用 getDeclaredMethods() 方法其实是比 getMethods() 方法的效率更高的,但有时会导致 NoClassDefFoundError,此时采取备用方案,再使用 getMethods() 进行获取;
  2. 循环遍历当前注册类的 methods 数组,筛选出符合条件:publicnon-staticnon-abstract 的,然后获取符合条件的方法的所有参数类型,如果参数个数符合 1 的要求且使用了 Subscribe 注解,则通过 FindState.checkAdd() 方法来判断 FindState 的 Map<Class, Object> anyMethodByEventType 集合中是否已经添加过以当前参数的类型 eventTypekey 的键值对,如没添加过则返回 true,随后创建一个 SubscriberMethod 对象,并添加到 FindState 的 List<SubscriberMethod> subscriberMethods 集合中。
2.2.1.3 SubscriberMethodFinder#FindState#checkAdd()
class SubscriberMethodFinder {static class FindState {final Map<Class, Object> anyMethodByEventType = new HashMap<>();boolean checkAdd(Method method, Class<?> eventType) {// 2级检查:第一级通过事件类型(较快速),第二级检查需具有完整签名。通常,订阅者不会监听相同事件类型的方法Object existing = anyMethodByEventType.put(eventType, method);if (existing == null) { // existing 为 null,说明之前的集合,没有当前要加入的订阅方法return true; // 直接返回 true} else {if (existing instanceof Method) {if (!checkAddWithMethodSignature((Method) existing, eventType)) {// Paranoia checkthrow new IllegalStateException();}// Put any non-Method object to "consume" the existing MethodanyMethodByEventType.put(eventType, this);}return checkAddWithMethodSignature(method, eventType);}}}
}

FindState#checkAdd() 方法中,将订阅事件的方法 Method 以方法的参数类型为 key 保存到 HashMap<Class, Object> anyMethodByEventType 集合中;同时还会调用 FindState#checkAddWithMethodSignature() 方法将方法以方法的签名(形式为:方法名>Event 类型名)为 key 保存到 HashMap<String, Class> subscriberClassByMethodKey 集合中。

2.2.2 EventBus#subscribe()

在 EventBus#register() 方法中通过 SubscriberMethodFinder#findSubscriberMethods() 方法,查找到当前要注册类及其父类中订阅了事件的方法集合 List subscriberMethods,随后循环遍历该方法集合,再通过 EventBus#subscribe() 方法完成注册:

public class EventBus {// 保存以订阅方法的参数类型 eventType 为 key,Subscription 对象集合为 value 的键值对的集合 HashMapprivate final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;// 保存以当前要注册类的对象为 key,注册类中订阅事件的方法的参数类型的集合为 value 的键值对的集合 HashMapprivate final Map<Object, List<Class<?>>> typesBySubscriber;// 必须在同步块中调用,EventBus 为方法的订阅过程进行了加锁,保证了线程安全private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {Class<?> eventType = subscriberMethod.eventType; // 得到当前订阅了事件的方法的参数类型// Subscription 类保存了要注册的类对象以及当前订阅的 subscriberMethod Subscription newSubscription = new Subscription(subscriber, subscriberMethod);// 查找集合 subscriptionsByEventType 中是否存在以当前 eventType 为 key 的值 CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);if (subscriptions == null) { // 如果不存在,则创建一个 subscriptions 集合,并以当前订阅方法的参数类型为 key 保存到 subscriptionsByEventTypesubscriptions = new CopyOnWriteArrayList<>();subscriptionsByEventType.put(eventType, subscriptions);} else {if (subscriptions.contains(newSubscription)) {throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "+ eventType);}}// 将新创建的 newSubscription 对象按照优先级 priority 的顺序添加到 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;}}// 查找是否存在当前要注册的类对象所对应的方法的参数类型集合List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);if (subscribedEvents == null) {// 不存在则创建一个集合 subscribedEvents,并以当前要注册类的对象为 key 保存到 typesBySubscribersubscribedEvents = 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);}}}
}

EventBus#subscribe() 方法中,新建 Subscription 实例对象保存要注册的类对象以及当前类中订阅的 subscriberMethod,将新建的 Subscription 实例对象保存到 Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType 集合中,同时将当前订阅事件方法的参数类型添加到 Map<Object, List<Class<?>>> typesBySubscriber 集合中。流程至此,EventBus 注册的核心流程的源码已经分析完。

3.EventBus 取消注册

在这里插入图片描述
结合 EventBus 取消注册流程图,再来分析源码,首先 EventBus 取消注册的方式如下:

EventBus.getDefault().unregister(this);

3.1 EventBus#unregister()

public class EventBus {// 保存以当前要注册类的对象为 key,注册类中订阅事件的方法的参数类型的集合为 value 的键值对的集合 HashMapprivate final Map<Object, List<Class<?>>> typesBySubscriber;// 取消给定订阅者所注册订阅的所有事件public synchronized void unregister(Object subscriber) {// 获取当前注册类对象对应的订阅事件方法的参数类型的集合 List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);if (subscribedTypes != null) {// 遍历订阅事件方法的参数类型集合,释放之前保存的当前注册类中的 Subscriptionfor (Class<?> eventType : subscribedTypes) {unsubscribeByEventType(subscriber, eventType);}// 集合中删除以 subscriber 为 key 的键值对typesBySubscriber.remove(subscriber);} else {Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());}}
}

EventBus#unregister() 方法,首先获取当前注册类对象对应的订阅事件方法的参数类型的集合,随后遍历订阅事件方法的参数类型集合,调用 EventBus#unsubscribeByEventType() 方法释放之前保存的当前注册类中的 Subscription。最后从 Map<Object, List<Class<?>>> typesBySubscriber 集合中删除以当前 subscriberkey 的键值对。

3.2 EventBus#unsubscribeByEventType()

public class EventBus {// 保存以订阅方法的参数类型 eventType 为 key,Subscription 对象集合为 value 的键值对的集合 HashMapprivate final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;// 只更新 subscriptionsByEventType 集合,不更新 typebysubscriber 集合,调用者必须更新 typebysubscriber 集合private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {// 获取当前订阅方法的参数类型所对应的 Subscription 集合 List<Subscription> subscriptions = subscriptionsByEventType.get(eventType);if (subscriptions != null) {int size = subscriptions.size();for (int i = 0; i < size; i++) { // 遍历 Subscription 集合 Subscription subscription = subscriptions.get(i);// 如果当前 subscription 对象对应的注册类对象和要取消注册的注册类对象相同// 则从 Subscription 集合中删除当前 subscription 对象 if (subscription.subscriber == subscriber) {subscription.active = false;subscriptions.remove(i);i--;size--;}}}}
}

EventBus#unsubscribeByEventType() 方法中,获取当前订阅方法的参数类型所对应的 Subscription 集合,遍历 Subscription 集合,如果当前 subscription 对象所对应的注册类对象和要取消注册的注册类对象相同,则从 Subscription 集合中删除。流程至此,EventBus 取消注册的源码已经分析完。

4.EventBus 发布、处理事件

EventBus 发布事件的方式如下:

EventBus.getDefault().post(new EventMessage(1, "事件 1"));

4.1 EventBus#post()

public class EventBus {// currentPostingThreadState 是一个 PostingThreadState 类型的 ThreadLocal // PostingThreadState 类保存了事件队列和线程模式等信息 private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {@Overrideprotected PostingThreadState initialValue() {return new PostingThreadState();}};    // 将给定的事件 event 发布到事件总线 EventBuspublic void post(Object event) {// 获取 ThreadLocal 中保存的 PostingThreadState 实例对象PostingThreadState postingState = currentPostingThreadState.get();List<Object> eventQueue = postingState.eventQueue;eventQueue.add(event); // 将要发送的事件添加到事件队列if (!postingState.isPosting) { // isPosting 默认为 false postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper(); // 是否为主线程postingState.isPosting = true; // isPosting 置为 true,使得事件 post 的过程中 当前线程的其他 post 事件无法被响应if (postingState.canceled) {throw new EventBusException("Internal error. Abort state was not reset");}try {while (!eventQueue.isEmpty()) { // 遍历事件队列 // EventBus.postSingleEvent() 发送单个事件 // eventQueue.remove(0),从事件队列移除事件 postSingleEvent(eventQueue.remove(0), postingState);}} finally {postingState.isPosting = false; // 当 post 过程结束后,再将 isPosting 置为 falsepostingState.isMainThread = false;}}}
}

EventBus#post() 方法,获取 ThreadLocal 中保存的 PostingThreadState 实例对象,将要发送的事件添加到事件队列 PostingThreadState.eventQueue 中,随后遍历事件队列,调用 EventBus#postSingleEvent() 方法发送单个事件。

4.2 EventBus#postSingleEvent()

public class EventBus { private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {Class<?> eventClass = event.getClass();boolean subscriptionFound = false;if (eventInheritance) { // eventInheritance默认为true,表示是否向上查找事件的父类// 查找当前事件类型 Class 及其父类、接口等保存到集合 Map<Class<?>, List<Class<?>>> eventTypesCachList<Class<?>> eventTypes = lookupAllEventTypes(eventClass);int countTypes = eventTypes.size();for (int h = 0; h < countTypes; h++) { // 遍历 eventTypesCach 集合,继续处理事件 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));}}}
}

EventBus#postSingleEvent() 方法中,根据 eventInheritance 属性,决定是否向上遍历事件的父类型,将获取到的当前事件类型 Class 及其父类、接口等保存到 Map<Class<?>, List<Class<?>>> eventTypesCach 集合中,然后遍历刚获取的集合,对集合中的每一个 Class 调用 EventBus#postSingleEventForEventType() 方法进一步处理。

4.3 EventBus#postSingleEventForEventType()

public class EventBus { // 保存以订阅方法的参数类型 eventType 为 key,Subscription 对象集合为 value 的键值对的集合 HashMapprivate final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;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; // PostingThreadState 记录事件postingState.subscription = subscription; // PostingThreadState 记录对应的 subscriptionboolean aborted = false;try {// EventBus.postToSubscription() 方法对事件进行处理 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;}
}

EventBus#postSingleEventForEventType() 方法中,首先同步加锁获取事件类型对应的 Subscription 集合,如果获得的集合不为 null,表示已有订阅者订阅了对应类型的事件,则遍历 Subscription 集合,为每一个 Subscription 调用 EventBus#postToSubscription() 方法来处理事件。

4.4 EventBus#postToSubscription()

public class EventBus { private final HandlerPoster mainThreadPoster;private final BackgroundPoster backgroundPoster;private final AsyncPoster asyncPoster;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;case MAIN_ORDERED: // 无论在那个线程发送事件,都先将事件入队列,然后通过 Handler 切换到主线程,依次处理事件if (mainThreadPoster != null) { // mainThreadPoster 不为空,将事件入队列,然后通过 Handler 切换到主线程,依次处理事件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);}}
}

EventBus#postToSubscription() 方法,根据订阅事件方法的线程模式、以及发送事件的线程来判断如何处理事件,处理方式主要有两种:

  1. 在相应线程直接通过 EventBus#invokeSubscriber() 方法,通过反射来执行订阅事件的方法,此时发送出去的事件就被订阅者接收并做相应处理;
  2. 通过不同的 Poster 将事件入队,然后采用队列的方式做进一步处理。
4.4.1 EventBus#invokeSubscriber()
public class EventBus { 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);}}
}

EventBus#invokeSubscriber() 方法中,由 Subscription 实例保存的事件订阅方法,通过反射来执行订阅者的事件订阅方法,此时发布的事件就被订阅者接收并做相应处理。

4.4.2 HandlerPoster#enqueue()
final class HandlerPoster extends Handler {private final PendingPostQueue queue;private final int maxMillisInsideHandleMessage;private final EventBus eventBus;private boolean handlerActive;HandlerPoster(EventBus eventBus, Looper looper, int maxMillisInsideHandleMessage) {super(looper);this.eventBus = eventBus;this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage;queue = new PendingPostQueue();}void enqueue(Subscription subscription, Object event) {// 通过 PendingPost.obtainPendingPost() 方法从 pendingPostPool 缓存中获取 PendingPost 并赋值// 如果缓存中没有,则由 subscription 和 event 新建一个 PendingPost 对象PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);synchronized (this) {queue.enqueue(pendingPost); // 通过 PendingPostQueue.enqueue() 入队if (!handlerActive) {handlerActive = true;// 发送开始处理事件的消息,handleMessage() 方法将被执行,完成从子线程到主线程的切换if (!sendMessage(obtainMessage())) {throw new EventBusException("Could not send handler message");}}}}@Overridepublic void handleMessage(Message msg) {boolean rescheduled = false;try {long started = SystemClock.uptimeMillis();while (true) { // 死循环遍历队列PendingPost pendingPost = queue.poll(); // 出队获取 PendingPostif (pendingPost == null) {synchronized (this) {// 再检查一次,这次是同步的pendingPost = queue.poll();if (pendingPost == null) {handlerActive = false; // 经过两次获取仍然为 null 则直接返回并置 handlerActive 为 falsereturn;}}}// 通过 EventBus.invokeSubscriber() 方法进一步处理 pendingPosteventBus.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;}}
}

HandlerPoster#enqueue() 方法,首先通过 PendingPost.obtainPendingPost() 方法从 pendingPostPool 缓存中获取 PendingPost 并赋值传入的 subscriptionevent 对象,如果缓存中获取不到,则由 subscriptionevent 新建一个 PendingPost 对象,并将 PendingPost 添加到 PendingPostQueue 队列中,随后通过 Handler 切换到主线程,在 Handler#handleMessage() 方法中将 PendingPost 对象循环出队列,交给 EventBus#invokeSubscriber() 方法进行处理。

4.4.3 EventBus#invokeSubscriber()
public class EventBus {void invokeSubscriber(PendingPost pendingPost) {Object event = pendingPost.event;Subscription subscription = pendingPost.subscription;PendingPost.releasePendingPost(pendingPost); // 释放 PendingPost 引用的资源if (subscription.active) {// 最终,通过反射来执行订阅事件的方法invokeSubscriber(subscription, event);}}
}

EventBus#invokeSubscriber() 方法,主要就是从 PendingPost 中取出之前保存的 eventsubscription,然后通过反射来执行订阅事件的方法,又回到了第一种处理方式。所以 HandlerPoster#enqueue(subscription, event) 方法的核心就是先将将事件入队列,然后通过 Handler 从子线程切换到主线程中去处理事件。

4.4.4 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.obtainPendingPost() 方法从 pendingPostPool 缓存中获取 PendingPost 并赋值// 如果缓存中没有,则由 subscription 和 event 新建一个 PendingPost 对象PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);synchronized (this) {queue.enqueue(pendingPost); // 通过 PendingPostQueue.enqueue() 入队if (!executorRunning) {executorRunning = true;// 通过线程池来执行当前 BackgroundPoster 的 run() 方法来进一步处理事件eventBus.getExecutorService().execute(this);}}}@Overridepublic void run() {try {try {while (true) { // 死循环遍历队列PendingPost pendingPost = queue.poll(1000); // 出队获取 PendingPostif (pendingPost == null) {synchronized (this) {// 再检查一次,这次是同步的pendingPost = queue.poll();if (pendingPost == null) {// 经过两次获取仍然为 null 则直接返回并置 handlerActive 为 falseexecutorRunning = false;return;}}}// 通过 EventBus.invokeSubscriber() 方法进一步处理 pendingPosteventBus.invokeSubscriber(pendingPost);}} catch (InterruptedException e) {Log.w("Event", Thread.currentThread().getName() + " was interruppted", e);}} finally {executorRunning = false;}}
}

BackgroundPoster#enqueue() 方法跟 HandlerPoster#enqueue() 方法的功能差不多,核心也是先将事件加入到 PendingPostQueue 队列,然后再出队列。不同之处是 BackgroundPoster 是通过线程池来执行其 run() 方法,最后交给 EventBus#invokeSubscriber() 方法进行处理。

4.4.5 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.obtainPendingPost() 方法从 pendingPostPool 缓存中获取 PendingPost 并赋值// 如果缓存中没有,则由 subscription 和 event 新建一个 PendingPost 对象PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);queue.enqueue(pendingPost); // 通过 PendingPostQueue.enqueue() 入队// 通过线程池来执行当前 AsyncPoster 的 run() 方法来进一步处理事件eventBus.getExecutorService().execute(this);}@Overridepublic void run() {PendingPost pendingPost = queue.poll();if(pendingPost == null) {throw new IllegalStateException("No pending post available");}// 通过 EventBus.invokeSubscriber() 方法进一步处理 pendingPosteventBus.invokeSubscriber(pendingPost);}
}

AsyncPoster#enqueue() 方法跟 HandlerPoster#enqueue() 方法差不多,核心也是先将事件加入到 PendingPostQueue 队列,然后再出队列,随后通过线程池来执行其 run() 方法,最后交给 EventBus#invokeSubscriber() 方法进行处理。流程至此,EventBus 发布事件与处理事件的源码已经分析完。

EventBus 发布事件(包括粘性事件)及处理流程

EventBus 发布事件及处理流程

5.EventBus 粘性事件

一般情况,使用 EventBus 都是准备好订阅事件的方法,然后注册事件,最后在发布事件,即要先有事件的接收者(订阅者)。但粘性事件却恰恰相反,可以先发布事件,后续再准备订阅事件的方法、以及注册事件。

发布粘性事件通过如下方式:

EventBus.getDefault().postSticky(new EventMessage(2, "粘性事件"));

5.1 EventBus#postSticky()

public class EventBus {// 发布粘性事件时,保存事件的类型和对应事件本身private final Map<Class<?>, Object> stickyEvents;// 将给定的事件发布到 EventBus 并保持该事件(因为它具有粘性)。// 相同事件类型、最近的粘性事件保存在内存中,供订阅者使用 Subscribe#sticky() 访问public void postSticky(Object event) {synchronized (stickyEvents) {// 将发布的事件和事件类型保存到 stickyEventsstickyEvents.put(event.getClass(), event);}// 保存后再调用 EventBus#post(event) 方法,以防订阅者想立即删除post(event);}
}

EventBus#postSticky() 方法执行流程如下:

  1. 将事件类型和对应事件保存到 Map<Class<?>, Object> stickyEvents 集合中,等待后续使用;
  2. 通过 EventBus#post(event) 方法继续发布事件。因此,如果在发布粘性事件前,已经有了对应类型事件的订阅者,即使它是非粘性的,依然可以接收到发布的粘性事件。

通过 EventBus#post(event) 方法发布粘性事件,流程在前面已经分析过,在前面分析 EventBus#subscribe() 方法时,关于粘性事件的处理过程还没分析,下面一起来剖析一下这段代码。

5.2 EventBus#subscribe()

public class EventBus {// 发布粘性事件时,保存事件的类型和对应事件本身private final Map<Class<?>, Object> stickyEvents;private final boolean eventInheritance; // 默认为 true,表示是否向上查找事件的父类// 必须在同步块中调用,EventBus 为方法的订阅过程进行了加锁,保证了线程安全private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {......// 如果当前订阅事件方法的 Subscribe 注解的 sticky 属性为 true,即该方法可接受粘性事件if (subscriberMethod.sticky) {if (eventInheritance) { // 默认为 true,表示向上查找事件的父类// 须考虑 eventType 所有子类的现有粘性事件。注意:对于粘性事件较多的情况,遍历所有事件效率不高// 因此要更改数据结构使得查找更加有效(如:存储父类的子类集合:Class -> List<Class>)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(); // 获得对应的事件// 通过 EventBus.checkPostStickyEventToSubscription() 方法处理粘性事件checkPostStickyEventToSubscription(newSubscription, stickyEvent);}}} else {Object stickyEvent = stickyEvents.get(eventType);// 通过 EventBus.checkPostStickyEventToSubscription() 方法处理粘性事件checkPostStickyEventToSubscription(newSubscription, stickyEvent);}}}
}

EventBus#subscribe() 方法中,如果当前订阅事件方法的 Subscribe 注解的 sticky 属性为 true,即该方法可接受粘性事件。遍历 EventBus#postSticky() 方法保存到 Map<Class<?>, Object> stickyEvents 集合中的粘性事件,如果 stickyEvents 中取出事件的事件类型与当前订阅方法接收的事件类型相同或者是其子类,则取出 stickyEvents 中对应事件类型的具体事件,然后通过 EventBus.checkPostStickyEventToSubscription() 方法处理粘性事件。

5.3 EventBus#checkPostStickyEventToSubscription()

public class EventBus {// 发布粘性事件时,保存事件的类型和对应事件本身private final Map<Class<?>, Object> stickyEvents;private final boolean eventInheritance; // 默认为 true,表示是否向上查找事件的父类private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {if (stickyEvent != null) {// 如果订阅者试图中止当前事件,则将失败(在发布状态下事件无法追踪)postToSubscription(newSubscription, stickyEvent, Looper.getMainLooper() == Looper.myLooper());}}
}

EventBus#checkPostStickyEventToSubscription() 方法,在判空操作通过后,通过 EventBus#postToSubscription() 方法完成粘性事件的处理,处理流程前面已经分析过,不再重复分析。流程至此,EventBus 粘性事件的发布与处理流程已经分析完。

6.EventBus 之 Subscriber Index

通过上面几节的分析可知,EventBus 在项目运行时默认是通过反射来查找订阅事件的方法信息,如果项目中存在大量的订阅事件的方法,通过反射必然会对项目运行时的性能产生影响。EventBus 也考虑到了这个问题,因此除了默认的反射之外,还提供了在项目编译时通过注解处理器(APT 全称:Annotation Processing Tool)查找订阅事件方法信息的方式,在编译期生成一个辅助的索引类 Subscriber Index 来保存这些信息。

要在项目编译时查找订阅事件的方法信息,首先要在 app 的 build.gradle 中加入如下配置:

android {defaultConfig {javaCompileOptions {annotationProcessorOptions {// 根据项目实际情况,指定辅助索引类的名称和包名arguments = [ eventBusIndex : 'com.xxx.xxx.EventBusIndex' ]}}}
}
dependencies {compile 'org.greenrobot:eventbus:3.2.0'// 引入注解处理器annotationProcessor 'org.greenrobot:eventbus-annotation-processor:3.2.0'
}

然后在项目的 Application 中添加如下配置,以生成一个默认的 EventBus 单例:

public class AndroidApplication extends Application implements Application.ActivityLifecycleCallbacks {@Overridepublic void onCreate() {super.onCreate();registerActivityLifecycleCallbacks(this);EventBus.builder().addIndex(new EventBusIndex()).installDefaultEventBus();}
}

用法还是和上面的例子一样,只是在项目编译时会生成对应的 EventBusIndex 类(这里是在项目的底层 baselibrary 进行配置的,进行了封装,如果是简单使用,放到 app 模块即可):
在这里插入图片描述

6.1 EventBusIndex

// 这个类是由 EventBus 生成的,不要编辑
public class EventBusIndex implements SubscriberInfoIndex {// 保存当前注册类的 Class 类型和其中事件订阅方法的信息private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX;static {SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>();putIndex(new SimpleSubscriberInfo(com.xxx.xxx.baselibrary.mvp.view.BaseMVPActivity.class, true,new SubscriberMethodInfo[] {new SubscriberMethodInfo("onEvent", java.util.Map.class, ThreadMode.MAIN),}));}private static void putIndex(SubscriberInfo info) {SUBSCRIBER_INDEX.put(info.getSubscriberClass(), info);}@Overridepublic SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) {SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass);if (info != null) {return info;} else {return null;}}
}

EventBusIndex 是由 EventBus 生成的,不要编辑。其内部首先在静态代码块内新建 HashMap 保存当前注册类的 Class 类型和其中事件订阅方法的信息,然后通过 EventBusIndex#putIndex() 方法将新建的 SimpleSubscriberInfo 实例对象添加到新建的 HashMap 中保存。

接下来通过源码剖析使用 Subscriber IndexEventBus 的注册流程,由前面的使用代码可知,期首先创建一个 EventBusBuilder 实例对象,然后通过其 addIndex() 方法添加索引类的实例:

6.2 EventBusBuilder#addIndex()

public class EventBusBuilder {List<SubscriberInfoIndex> subscriberInfoIndexes;/** Adds an index generated by EventBus' annotation preprocessor. */// 添加一个由 EventBus 注解处理器生成的索引 SubscriberInfoIndexpublic EventBusBuilder addIndex(SubscriberInfoIndex index) {if(subscriberInfoIndexes == null) {subscriberInfoIndexes = new ArrayList<>();}subscriberInfoIndexes.add(index);return this;}
}

EventBusBuilder#addIndex() 方法把生成的索引类的实例保存在 List<SubscriberInfoIndex> subscriberInfoIndexes 集合中。

6.2 EventBusBuilder#installDefaultEventBus()

public class EventBusBuilder {/*** 安装由 EventBus#getDefault() 方法返回的默认 EventBus(使用 EventBusBuilder 的值)* 必须在第一次使用默认 EventBus 之前完成一次** 如果已经有一个默认的 EventBus 实例,抛出 EventBusException*/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;}}// 基于当前配置构建 EventBus 实例对象public EventBus build() {// this 代表当前 EventBusBuilder 对象return new EventBus(this);}
}

EventBusBuilder#installDefaultEventBus() 方法就是使用当前 EventBusBuilder 实例对象构建一个 EventBus 实例,然后赋值给 EventBusdefaultInstance 成员变量,这样通过 EventBusBuilder 配置的 Subscriber Index 也就传递到了 EventBus 实例中。

6.3 SubscriberMethodFinder#findUsingInfo()

由于配置使用了 Subscriber Index 索引类,回头再看 SubscriberMethodFinder#findUsingInfo() 方法,执行流程将会有所不同:

class SubscriberMethodFinder {private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {// 通过 SubscriberMethodFinder#prepareFindState() 方法从 FindState 池中获取到非空的 FindState 并返回FindState findState = prepareFindState();findState.initForSubscriber(subscriberClass); // 初始化 FindState// 初始状态下 findState.clazz 就是 subscriberClass while (findState.clazz != null) {// 虽然 FindState.initForSubscriber() 方法初始化时 subscriberInfo 赋值为 null// 但此时通过 EventBusBuilder 向 List<SubscriberInfoIndex> subscriberInfoIndexes 集合中添加了// SubscriberInfoIndex 的实现类 EventBusIndex 实例对象,因此,getSubscriberInfo() 方法将返回// EventBusIndex#getSubscriberInfo() 方法的返回值,这里将返回 SimpleSubscriberInfo 实例对象// 该实例对象是在 EventBusIndex 初始化时创建的,将返回的该实例对象赋值给 FindState.subscriberInfo findState.subscriberInfo = getSubscriberInfo(findState);if (findState.subscriberInfo != null) { // findState.subscriberInfo 此时不为 null// 通过 SimpleSubscriberInfo.getSubscriberMethods() 方法获得当前注册类中所有订阅了事件的方法SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();for (SubscriberMethod subscriberMethod : array) {// FindState.checkAdd()方法用来判断 FindState 的 anyMethodByEventType map 是否// 已经添加过以当前 eventType 为 key 的键值对,没添加过则返回 true if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {// 将 SubscriberMethod 对象,添加到 subscriberMethods 集合 findState.subscriberMethods.add(subscriberMethod);}}} else { // 通过反射查找订阅事件的方法 findUsingReflectionInSingleClass(findState);}// 修改 findState.clazz 为 subscriberClass 的父类 Class,即需要遍历父类findState.moveToSuperclass();}// 查找到的方法保存在了 FindState 实例的 subscriberMethods 集合中// 使用 FindState.subscriberMethods 构建一个新的 List<SubscriberMethod>,然后释放掉 FindStatereturn getMethodsAndRelease(findState);}
}

SubscriberMethodFinder#findUsingInfo() 方法中,由于配置使用了 Subscriber Index,此时 SubscriberMethodFinder#getSubscriberInfo() 方法的返回值不再为 null,具体来看一下该方法的执行流程。

6.3.1 SubscriberMethodFinder#getSubscriberInfo()
class SubscriberMethodFinder {private SubscriberInfo getSubscriberInfo(FindState findState) {// FindState.initForSubscriber() 方法初始化时 subscriberInfo 赋值为 null,因此该条件不成立if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) {SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo();if (findState.clazz == superclassInfo.getSubscriberClass()) {return superclassInfo;}}// 通过 EventBusBuilder 向 List<SubscriberInfoIndex> subscriberInfoIndexes 集合中添加了// SubscriberInfoIndex 的实现类 EventBusIndex 实例对象if (subscriberInfoIndexes != null) { // subscriberInfoIndexes 不为 nullfor (SubscriberInfoIndex index : subscriberInfoIndexes) { // 遍历索引类实例集合// 根据注册类的 Class 类查找 SubscriberInfoSubscriberInfo info = index.getSubscriberInfo(findState.clazz);if (info != null) {return info;}}}return null;}
}

SubscriberMethodFinder#getSubscriberInfo() 方法,遍历 List<SubscriberInfoIndex> subscriberInfoIndexes 集合根据注册类的 Class 类查找 SubscriberInfo,此时将返回 EventBusIndex 初始化时创建的 SimpleSubscriberInfo 实例对象。

6.3.2 SimpleSubscriberInfo#getSubscriberMethods()
public class SimpleSubscriberInfo extends AbstractSubscriberInfo {@Overridepublic synchronized SubscriberMethod[] getSubscriberMethods() {int length = methodInfos.length;SubscriberMethod[] methods = new SubscriberMethod[length];for (int i = 0; i < length; i++) {SubscriberMethodInfo info = methodInfos[i];methods[i] = createSubscriberMethod(info.methodName, info.eventType, info.threadMode,info.priority, info.sticky);}return methods;}
}

SimpleSubscriberInfo#getSubscriberMethods() 方法的主要作用是获取当前注册类中所有订阅了事件的方法。

流程至此,EventBus 使用 Subscriber Index 的主要源码分析完毕,其它的和之前的注册订阅流程一样。

小结

Subscriber Index 的核心就是项目编译时使用注解处理器生成保存事件订阅方法信息的索引类,然后项目运行时将索引类实例设置到 EventBus 中,这样当注册 EventBus 时,从索引类取出当前注册类对应的事件订阅方法信息,以完成最终的注册,避免了运行时反射处理的过程,所以在性能上会有质的提高。项目中可以根据实际的需求决定是否使用 Subscriber Index


总结

  • EventBus 底层采用的是注解和反射的方式来获取订阅方法信息;
  • 整个 EventBus 可以看出,事件是被观察者,订阅者类是观察者,当事件出现或者发布变更的时候,会通过 EventBus 通知观察者,使得观察者的订阅方法能够被自动调用;

注意:项目中根据实际需求决定是否使用,如果滥用的话,各种逻辑交叉在一起,也可能会给后期的维护带来困难。

参考

greenrobot/EventBus:github.com/greenrobot/EventBus

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/45512.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ipynb转换为pdf、Markdown(.md)

Jupyter Notebook 文件&#xff08;.ipynb&#xff09;可以转换成多种数据格式&#xff0c;以适应不同的使用场景和需求。以下是几种常见的转换格式及其简洁描述&#xff1a; HTML: Jupyter Notebook可以直接导出为静态的网页&#xff08;HTML&#xff09;格式&#xff0c;这样…

记一次IP数据处理过程,文本(CSV文件)处理,IP解析

个人博客&#xff1a;无奈何杨&#xff08;wnhyang&#xff09; 个人语雀&#xff1a;wnhyang 共享语雀&#xff1a;在线知识共享 Github&#xff1a;wnhyang - Overview 起因 突然接收到XX给的任务&#xff0c;要将一批IP数据处理一下&#xff0c;将IP对应的省市区解析出来…

PHP智云物业管理平台微信小程序系统源码

​&#x1f3e0;智云物业管理新纪元&#xff01;微信小程序&#xff0c;让家园管理更智慧&#x1f4f1; &#x1f3e1;【开篇&#xff1a;智慧生活&#xff0c;从物业开始】&#x1f3e1; 在快节奏的现代生活中&#xff0c;我们追求的不仅仅是家的温馨&#xff0c;更是生活的…

基于hive数据库的泰坦尼克号幸存者数据分析

进入 ./beeline -u jdbc:hive2://node2:10000 -n root -p 查询 SHOW TABLES; 删除 DROP TABLE IF EXISTS tidanic; 上传数据 hdfs dfs -put train.csv /user/hive/warehouse/mytrain.db/tidanic 《泰坦尼克号幸存者数据分析》 1、原始数据介绍 泰坦尼克号是当时世界上…

达梦数据库系列—28. 主备集群高可用测试

目录 监视器关闭 监视器启动&#xff0c;Detach备库 主备正常&#xff0c;手动switchover 主库故障&#xff0c;自动switchover 主库故障&#xff0c;手动Takeover 主库故障&#xff0c;备库强制takeover 主库重启 备库故障 公网连接异常 主库私网异常 备库私网异常…

实现给Nginx的指定网站开启basic认证——http基本认证

一、问题描述 目前我们配置的网站内容都是没有限制&#xff0c;可以让任何人打开浏览器都能够访问&#xff0c;这样就会存在一个问题&#xff08;可能会存在一些恶意访问的用户进行恶意操作&#xff0c;直接访问到我们的敏感后台路径进行操作&#xff0c;风险就会很大&#xff…

Why can‘t I access GPT-4 models via API, although GPT-3.5 models work?

题意&#xff1a;为什么我无法通过API访问GPT-4模型&#xff0c;尽管GPT-3.5模型可以工作&#xff1f; 问题背景&#xff1a; Im able to use the gpt-3.5-turbo-0301 model to access the ChatGPT API, but not any of the gpt-4 models. Here is the code I am using to tes…

【雷丰阳-谷粒商城 】【分布式高级篇-微服务架构篇】【22】【RabbitMQ】

持续学习&持续更新中… 守破离 【雷丰阳-谷粒商城 】【分布式高级篇-微服务架构篇】【22】【RabbitMQ】 Message Queue 消息队列异步处理应用解耦流量控制 消息中间件概念RabbitMQ概念MessagePublisherExchangeQueueBindingConnectionChannelConsumerVirtual HostBroker图…

Django prefetch_related()方法

prefetch_related的作用 prefetch_related()是 Django ORM 中用于优化查询性能的另一个重要方法&#xff0c;尤其在处理多对多&#xff08;ManyToMany&#xff09;关系和反向关系时非常有用。它允许你预加载相关对象&#xff0c;从而减少数据库查询次数。 1&#xff0c;创建应…

【香橙派】Orange pi AIpro开发板使用之一键部署springboot项目

前言 最近有幸收到一份新款 OrangePi AIpro 开发板&#xff0c;之前手里也捣鼓过一些板子&#xff0c;这次尝试从零开始部署一个简单的后端服务。OrangePi AIpro 采用昇腾AI技术路线&#xff0c;具体为4核64位处理器AI处理器&#xff0c;可配16GB内存容量&#xff0c;各种复杂应…

数字化赋能,加油小程序让出行更便捷高效

在快节奏的现代生活中&#xff0c;每一次加油不仅是车辆续航的必要步骤&#xff0c;也成为了人们日常生活中不可或缺的一环。随着科技的飞速发展&#xff0c;传统加油模式正逐步向智能化、便捷化转型&#xff0c;其中&#xff0c;加油小程序作为这股浪潮中的佼佼者&#xff0c;…

el-date-picker手动输入日期,通过设置开始时间和阶段自动填写结束时间

需求&#xff1a;根据开始时间&#xff0c;通过填写阶段时长&#xff0c;自动填写结束时间&#xff0c;同时开始时间和节数时间可以手动输入 代码如下&#xff1a; <el-form ref"ruleForm2" :rules"rules2" :model"formData" inline label-po…

yolov5 上手

0 介绍 YOLO(You Only Look Once)是一种流行的物体检测和图像分割模型&#xff0c;由华盛顿大学的约瑟夫-雷德蒙&#xff08;Joseph Redmon&#xff09;和阿里-法哈迪&#xff08;Ali Farhadi&#xff09;开发。YOLO 于 2015 年推出&#xff0c;因其高速度和高精确度而迅速受到…

人工智能算法工程师(中级)课程13-神经网络的优化与设计之梯度问题及优化与代码详解

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下人工智能算法工程师(中级)课程13-神经网络的优化与设计之梯度问题及优化与代码详解。 文章目录 一、引言二、梯度问题1. 梯度爆炸梯度爆炸的概念梯度爆炸的原因梯度爆炸的解决方案 2. 梯度消失梯度消失的概念梯度…

powerdesigner导出表数据库设计文档excel

1、连接数据库&#xff0c;导出表结构的sql脚本 2、打开powerdesigner&#xff0c;生成项目空间表 sql脚本用第一步的脚本 3、用script脚本生成excel 脚本信息 Option Explicit Dim rowsNum rowsNum 0 -------------------------------------------------------------…

CV12_ONNX转RKNN模型(谛听盒子)

暂时简单整理一下&#xff1a; 1.在边缘设备上配置相关环境。 2.配置完成后&#xff0c;获取模型中间的输入输出结果&#xff0c;保存为npy格式。 3.将onnx格式的模型&#xff0c;以及中间输入输出文件传送到边缘设备上。 4.编写一个python文件用于转换模型格式&#xff0c…

[iOS]内存分区

[iOS]内存分区 文章目录 [iOS]内存分区五大分区栈区堆区全局区常量区代码区验证内存使用注意事项总结 函数栈堆栈溢出栈的作用 参考博客 在iOS中&#xff0c;内存主要分为栈区、堆区、全局区、常量区、代码区五大区域 还记得OC是C的超类 所以C的内存分区也是一样的 iOS系统中&a…

51单片机STC89C52RC——19.1 SG90舵机(伺服电机)

目的/效果 独立按键K1&#xff0c;K2 实现加舵机减角度增减&#xff0c;LCD1602显示舵机转角度数&#xff08;上电默认90度&#xff09; 一&#xff0c;STC单片机模块 二&#xff0c;SG90舵机 2.1 简介 舵机只是我们通俗的叫法&#xff0c;它的本质是一个伺服电机&#xf…

react 案例的实现

先看一下如下效果 效果 这是一个 简单的 效果 左边是用户名进行登录 右边是一个答题还有遮罩 相信大家还有刚刚创建好的 react 脚手架了&#xff0c;没有的话可以运行以下命令 creact-react-app 项目名称 把项目名称四个字 改成 自己想要的一个名字 最好是英文的在 App.js中去…

Web开发:<br>标签的作用

br作用 介绍基本用法常见用途注意事项使用CSS替代 介绍 在Web开发中&#xff0c;<br> 标签是一个用于插入换行符的HTML标签。它是“break”的缩写&#xff0c;常用于需要在文本中强制换行的地方。<br> 标签是一个空标签&#xff0c;这意味着它没有结束标签。 基本…