文章目录
- 前言
- [一、EventBus 简介](#一、EventBus 简介)
-
- [EventBus 三要素](#EventBus 三要素)
- [EventBus 线程模型](#EventBus 线程模型)
- [二、EventBus 使用](#二、EventBus 使用)
-
- 1.添加依赖
- [2.EventBus 基本使用](#2.EventBus 基本使用)
-
- [2.1 定义事件类](#2.1 定义事件类)
- [2.2 注册 EventBus](#2.2 注册 EventBus)
- [2.3 EventBus 发起通知](#2.3 EventBus 发起通知)
- [三、EventBus 源码详解](#三、EventBus 源码详解)
-
- [1.Subscribe 注解](#1.Subscribe 注解)
- 2.注册事件订阅方法
-
- [2.1 EventBus 实例](#2.1 EventBus 实例)
- [2.2 EventBus 注册](#2.2 EventBus 注册)
- [3.EventBus 取消注册](#3.EventBus 取消注册)
- [4.EventBus 发布、处理事件](#4.EventBus 发布、处理事件)
-
- 4.1 EventBus#post()
- 4.2 EventBus#postSingleEvent()
- 4.3 EventBus#postSingleEventForEventType()
- 4.4 EventBus#postToSubscription()
- [**EventBus** 发布事件(包括粘性事件)及处理流程](#EventBus 发布事件(包括粘性事件)及处理流程)
- [5.EventBus 粘性事件](#5.EventBus 粘性事件)
- [6.EventBus 之 Subscriber Index](#6.EventBus 之 Subscriber Index)
- 总结
- 参考
前言
在 Android 项目开发的时候,经常会遇到组件与组件之间、组件与后台线程之间的通信, 比如:子线程中进行数据请求,请求数据成功后,通过 Handler 、RxJava 等来通知 UI 主线程进行更新操作;两个 Fragment 之间可以通过 Listener 进行通信,简单的通信通过上述的技术手段也是可以满足需求的,但随着项目的不断迭代更新,程序越来越庞大时,就会要写很多的代码,从而导致代码严重的耦合问题。为了优化该问题,EventBus 事件总线应运而生。
一、EventBus 简介
EventBus 事件总线,是一款由 GreenRobot 开源的在 Android 开发中使用的发布/订阅事件总线框架,基于观察者模式,将事件的接收者和发送者分开解耦。用来替代广播 BroadCastReceiver 、startActivityForResult 、Handler 和异步回调等来实现各组件间、组件与后台线程间的通信。
EventBus 优点:
- 简化组件之间的通讯方式;
- 对通信双方进行解藕;
- 通过 ThreadMode 灵活切换工作线程;
- 速度快、性能好、库比较小、不占内存;
首先看一下官方给出的 EventBus 原理图:
Publisher 使用 post() 函数发出一个 Event 事件,Subscriber 在 onEvent() 函数中接收事件、进行后续的处理。
EventBus 三要素
使用 EventBus 时的三个重要参与者:Event 、Publisher 、Subscriber。
- Event:事件,它可以是任意类型;
- Publisher :事件的发布者,可以在任意线程里发布事件,一般情况下,使用 EventBus.getDefault() 方法就可以得到一个 EventBus 对象,然后再调用其 post(Object) 方法即可;
- Subscriber :事件订阅者,在 EventBus3.0 之前我们必须定义以 onEvent 开头的那几个方法,分别是:onEvent 、onEventMainThread 、onEventBackgroundThread 和 onEventAsync ,而在 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 文件中导入依赖库:
java
imlementation 'org.greenrobot:eventbus:3.2.0'
2.EventBus 基本使用
通过 EventBus 的三个重要参与者:Event 、Subscriber 、Publisher 来一步步学习 EventBus 的基本使用,
2.1 定义事件类
java
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;
}
@Override
public String toString() {
return "EventMessage{" + "code=" + code + ", data=" + data + '}';
}
}
2.2 注册 EventBus
java
public class EventBusActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
protected void onStart() {
super.onStart();
// 注册 EventBus
EventBus.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());
}
@Override
protected void onDestroy() {
super.onDestroy();
// 解除注册的 EventBus
EventBus.getDefault().unregister(this);
}
}
2.3 EventBus 发起通知
通过 EventBus#post(eventMessage) 方法或者 EventBus#postSticky(eventMessage) 方法来发起事件:
java
@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 注解配置事件订阅方法,其共有三个参数(可选):threadMode 、boolean sticky 、int priority。 完整的写法如下:
java
@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 注解的实现:
java
@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 三个属性值。
2.注册事件订阅方法
结合 EventBus 注册事件流程图,便于更好的理解源码执行流程,首先 EventBus 注册事件的方式如下:
java
EventBus.getDefault().register(this);
EventBus#getDefault() 是一个单例方法,保证当前只有一个 EventBus 实例对象:
2.1 EventBus 实例
java
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;
}
}
如果 defaultInstance 为 null ,则新建一个 EventBus 实例对象赋值给 defaultInstance 。最终通过调用 EventBus 的另一个有参构造函数,传入默认的 EventBusBuilder 来完成其相关属性的初始化。
可以通过 Builder 模式来支持用 EventBusBuilder 对 EventBus 进行一些属性的配置,例如用如下方式注册事件:
java
EventBus.builder()
.eventInheritance(false)
.logSubscriberExceptions(false)
.build()
.register(this);
2.2 EventBus 注册
java
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()
java
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 属性默认为 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;
}
}
}
SubscriberMethodFinder#findSubscriberMethods() 方法的执行流程如下:
- 先从缓存集合 Map<Class<?>, List> METHOD_CACHE 中查找、获取 subscriberClass 类对应的 SubscriberMethod 的集合,如果找到则直接返回;
- 如果查找不到,则根据条件判断进行下一步的查找过程,由于使用了默认的 EventBusBuilder ,因此 ignoreGeneratedIndex 属性默认为 false ,即忽略注解生成器,所以调用 SubscriberMethodFinder#findUsingInfo() 方法进行查找,最后将查找到的订阅事件的方法集合缓存到 METHOD_CACHE 中。
2.2.1.1 SubscriberMethodFinder#findUsingInfo()
java
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
findState.subscriberInfo = getSubscriberInfo(findState);
if (findState.subscriberInfo != null) { // 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 集合中
// 使用 FindState.subscriberMethods 构建一个新的 List<SubscriberMethod>,然后释放掉 FindState
return getMethodsAndRelease(findState);
}
}
通过注释可知在 SubscriberMethodFinder#findUsingInfo() 方法会在当前要注册的类以及其父类中查找订阅事件的方法,FindState 类是 SubscriberMethodFinder 的内部类,用来辅助查找订阅事件的方法,通过条件判断可知,接下来将通过 SubscriberMethodFinder#findUsingReflectionInSingleClass() 方法通过反射查找订阅事件的方法。
SubscriberMethodFinder 类中维护了一个 FindState 池,是一个默认大小为 4 的数组,通过 SubscriberMethodFinder#prepareFindState() 方法遍历该数组找到非 null 的 FindState 进行返回。而在 SubscriberMethodFinder#getMethodsAndRelease(findState) 方法中则是将搜寻的结果取出后,对 FindState 进行 recycle ,之后再将其放回 FindState 池中。
2.2.1.2 SubscriberMethodFinder#findUsingReflectionInSingleClass()
java
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() 方法的执行流程如下:
- 获取订阅事件的 Method 列表,注意:使用 getDeclaredMethods() 方法其实是比 getMethods() 方法的效率更高的,但有时会导致 NoClassDefFoundError,此时采取备用方案,再使用 getMethods() 进行获取;
- 循环遍历当前注册类的 methods 数组,筛选出符合条件:public 、non-static 、non-abstract 的,然后获取符合条件的方法的所有参数类型,如果参数个数符合 1 的要求且使用了 Subscribe 注解,则通过 FindState.checkAdd() 方法来判断 FindState 的 Map<Class, Object> anyMethodByEventType 集合中是否已经添加过以当前参数的类型 eventType 为 key 的键值对,如没添加过则返回 true ,随后创建一个 SubscriberMethod 对象,并添加到 FindState 的 List<SubscriberMethod> subscriberMethods 集合中。
2.2.1.3 SubscriberMethodFinder#FindState#checkAdd()
java
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 check
throw new IllegalStateException();
}
// Put any non-Method object to "consume" the existing Method
anyMethodByEventType.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() 方法完成注册:
java
public class EventBus {
// 保存以订阅方法的参数类型 eventType 为 key,Subscription 对象集合为 value 的键值对的集合 HashMap
private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
// 保存以当前要注册类的对象为 key,注册类中订阅事件的方法的参数类型的集合为 value 的键值对的集合 HashMap
private 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 保存到 subscriptionsByEventType
subscriptions = 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 保存到 typesBySubscriber
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);
}
}
}
}
EventBus#subscribe() 方法中,新建 Subscription 实例对象保存要注册的类对象以及当前类中订阅的 subscriberMethod ,将新建的 Subscription 实例对象保存到 Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType 集合中,同时将当前订阅事件方法的参数类型添加到 Map<Object, List<Class<?>>> typesBySubscriber 集合中。流程至此,EventBus 注册的核心流程的源码已经分析完。
3.EventBus 取消注册
结合 EventBus 取消注册流程图,再来分析源码,首先 EventBus 取消注册的方式如下:
java
EventBus.getDefault().unregister(this);
3.1 EventBus#unregister()
java
public class EventBus {
// 保存以当前要注册类的对象为 key,注册类中订阅事件的方法的参数类型的集合为 value 的键值对的集合 HashMap
private final Map<Object, List<Class<?>>> typesBySubscriber;
// 取消给定订阅者所注册订阅的所有事件
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 {
Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
}
EventBus#unregister() 方法,首先获取当前注册类对象对应的订阅事件方法的参数类型的集合,随后遍历订阅事件方法的参数类型集合,调用 EventBus#unsubscribeByEventType() 方法释放之前保存的当前注册类中的 Subscription 。最后从 Map<Object, List<Class<?>>> typesBySubscriber 集合中删除以当前 subscriber 为 key 的键值对。
3.2 EventBus#unsubscribeByEventType()
java
public class EventBus {
// 保存以订阅方法的参数类型 eventType 为 key,Subscription 对象集合为 value 的键值对的集合 HashMap
private 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 发布事件的方式如下:
java
EventBus.getDefault().post(new EventMessage(1, "事件 1"));
4.1 EventBus#post()
java
public class EventBus {
// currentPostingThreadState 是一个 PostingThreadState 类型的 ThreadLocal
// PostingThreadState 类保存了事件队列和线程模式等信息
private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
@Override
protected PostingThreadState initialValue() {
return new PostingThreadState();
}
};
// 将给定的事件 event 发布到事件总线 EventBus
public 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 置为 false
postingState.isMainThread = false;
}
}
}
}
EventBus#post() 方法,获取 ThreadLocal 中保存的 PostingThreadState 实例对象,将要发送的事件添加到事件队列 PostingThreadState.eventQueue 中,随后遍历事件队列,调用 EventBus#postSingleEvent() 方法发送单个事件。
4.2 EventBus#postSingleEvent()
java
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<?>>> eventTypesCach
List<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()
java
public class EventBus {
// 保存以订阅方法的参数类型 eventType 为 key,Subscription 对象集合为 value 的键值对的集合 HashMap
private 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 记录对应的 subscription
boolean 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()
java
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() 方法,根据订阅事件方法的线程模式、以及发送事件的线程来判断如何处理事件,处理方式主要有两种:
- 在相应线程直接通过 EventBus#invokeSubscriber() 方法,通过反射来执行订阅事件的方法,此时发送出去的事件就被订阅者接收并做相应处理;
- 通过不同的 Poster 将事件入队,然后采用队列的方式做进一步处理。
4.4.1 EventBus#invokeSubscriber()
java
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()
java
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");
}
}
}
}
@Override
public void handleMessage(Message msg) {
boolean rescheduled = false;
try {
long started = SystemClock.uptimeMillis();
while (true) { // 死循环遍历队列
PendingPost pendingPost = queue.poll(); // 出队获取 PendingPost
if (pendingPost == null) {
synchronized (this) {
// 再检查一次,这次是同步的
pendingPost = queue.poll();
if (pendingPost == null) {
handlerActive = false; // 经过两次获取仍然为 null 则直接返回并置 handlerActive 为 false
return;
}
}
}
// 通过 EventBus.invokeSubscriber() 方法进一步处理 pendingPost
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;
}
}
}
HandlerPoster#enqueue() 方法,首先通过 PendingPost.obtainPendingPost() 方法从 pendingPostPool 缓存中获取 PendingPost 并赋值传入的 subscription 、event 对象,如果缓存中获取不到,则由 subscription 和 event 新建一个 PendingPost 对象,并将 PendingPost 添加到 PendingPostQueue 队列中,随后通过 Handler 切换到主线程,在 Handler#handleMessage() 方法中将 PendingPost 对象循环出队列,交给 EventBus#invokeSubscriber() 方法进行处理。
4.4.3 EventBus#invokeSubscriber()
java
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 中取出之前保存的 event 、subscription ,然后通过反射来执行订阅事件的方法,又回到了第一种处理方式。所以 HandlerPoster#enqueue(subscription, event) 方法的核心就是先将将事件入队列,然后通过 Handler 从子线程切换到主线程中去处理事件。
4.4.4 BackgroundPoster#enqueue()
java
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);
}
}
}
@Override
public void run() {
try {
try {
while (true) { // 死循环遍历队列
PendingPost pendingPost = queue.poll(1000); // 出队获取 PendingPost
if (pendingPost == null) {
synchronized (this) {
// 再检查一次,这次是同步的
pendingPost = queue.poll();
if (pendingPost == null) {
// 经过两次获取仍然为 null 则直接返回并置 handlerActive 为 false
executorRunning = false;
return;
}
}
}
// 通过 EventBus.invokeSubscriber() 方法进一步处理 pendingPost
eventBus.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()
java
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);
}
@Override
public void run() {
PendingPost pendingPost = queue.poll();
if(pendingPost == null) {
throw new IllegalStateException("No pending post available");
}
// 通过 EventBus.invokeSubscriber() 方法进一步处理 pendingPost
eventBus.invokeSubscriber(pendingPost);
}
}
AsyncPoster#enqueue() 方法跟 HandlerPoster#enqueue() 方法差不多,核心也是先将事件加入到 PendingPostQueue 队列,然后再出队列,随后通过线程池来执行其 run() 方法,最后交给 EventBus#invokeSubscriber() 方法进行处理。流程至此,EventBus 发布事件与处理事件的源码已经分析完。
EventBus 发布事件(包括粘性事件)及处理流程
5.EventBus 粘性事件
一般情况,使用 EventBus 都是准备好订阅事件的方法,然后注册事件,最后在发布事件,即要先有事件的接收者(订阅者)。但粘性事件却恰恰相反,可以先发布事件,后续再准备订阅事件的方法、以及注册事件。
发布粘性事件通过如下方式:
java
EventBus.getDefault().postSticky(new EventMessage(2, "粘性事件"));
5.1 EventBus#postSticky()
java
public class EventBus {
// 发布粘性事件时,保存事件的类型和对应事件本身
private final Map<Class<?>, Object> stickyEvents;
// 将给定的事件发布到 EventBus 并保持该事件(因为它具有粘性)。
// 相同事件类型、最近的粘性事件保存在内存中,供订阅者使用 Subscribe#sticky() 访问
public void postSticky(Object event) {
synchronized (stickyEvents) {
// 将发布的事件和事件类型保存到 stickyEvents
stickyEvents.put(event.getClass(), event);
}
// 保存后再调用 EventBus#post(event) 方法,以防订阅者想立即删除
post(event);
}
}
EventBus#postSticky() 方法执行流程如下:
- 将事件类型和对应事件保存到 Map<Class<?>, Object> stickyEvents 集合中,等待后续使用;
- 通过 EventBus#post(event) 方法继续发布事件。因此,如果在发布粘性事件前,已经有了对应类型事件的订阅者,即使它是非粘性的,依然可以接收到发布的粘性事件。
通过 EventBus#post(event) 方法发布粘性事件,流程在前面已经分析过,在前面分析 EventBus#subscribe() 方法时,关于粘性事件的处理过程还没分析,下面一起来剖析一下这段代码。
5.2 EventBus#subscribe()
java
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()
java
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 中加入如下配置:
groovy
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 单例:
java
public class AndroidApplication extends Application implements Application.ActivityLifecycleCallbacks {
@Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(this);
EventBus.builder().addIndex(new EventBusIndex()).installDefaultEventBus();
}
}
用法还是和上面的例子一样,只是在项目编译时会生成对应的 EventBusIndex 类(这里是在项目的底层 baselibrary 进行配置的,进行了封装,如果是简单使用,放到 app 模块即可):
6.1 EventBusIndex
java
// 这个类是由 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);
}
@Override
public 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 Index 时 EventBus 的注册流程,由前面的使用代码可知,期首先创建一个 EventBusBuilder 实例对象,然后通过其 addIndex() 方法添加索引类的实例:
6.2 EventBusBuilder#addIndex()
java
public class EventBusBuilder {
List<SubscriberInfoIndex> subscriberInfoIndexes;
/** Adds an index generated by EventBus' annotation preprocessor. */
// 添加一个由 EventBus 注解处理器生成的索引 SubscriberInfoIndex
public EventBusBuilder addIndex(SubscriberInfoIndex index) {
if(subscriberInfoIndexes == null) {
subscriberInfoIndexes = new ArrayList<>();
}
subscriberInfoIndexes.add(index);
return this;
}
}
EventBusBuilder#addIndex() 方法把生成的索引类的实例保存在 List<SubscriberInfoIndex> subscriberInfoIndexes 集合中。
6.2 EventBusBuilder#installDefaultEventBus()
java
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 实例,然后赋值给 EventBus 的 defaultInstance 成员变量,这样通过 EventBusBuilder 配置的 Subscriber Index 也就传递到了 EventBus 实例中。
6.3 SubscriberMethodFinder#findUsingInfo()
由于配置使用了 Subscriber Index 索引类,回头再看 SubscriberMethodFinder#findUsingInfo() 方法,执行流程将会有所不同:
java
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>,然后释放掉 FindState
return getMethodsAndRelease(findState);
}
}
SubscriberMethodFinder#findUsingInfo() 方法中,由于配置使用了 Subscriber Index ,此时 SubscriberMethodFinder#getSubscriberInfo() 方法的返回值不再为 null,具体来看一下该方法的执行流程。
6.3.1 SubscriberMethodFinder#getSubscriberInfo()
java
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 不为 null
for (SubscriberInfoIndex index : subscriberInfoIndexes) { // 遍历索引类实例集合
// 根据注册类的 Class 类查找 SubscriberInfo
SubscriberInfo 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()
java
public class SimpleSubscriberInfo extends AbstractSubscriberInfo {
@Override
public 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 通知观察者,使得观察者的订阅方法能够被自动调用;
注意:项目中根据实际需求决定是否使用,如果滥用的话,各种逻辑交叉在一起,也可能会给后期的维护带来困难。