这可能是最详细的 EventBus 源码分析01 - EventBus 对象的创建
这可能是最详细的 EventBus 源码分析02 - EventBus 的注册(上篇)
这可能是最详细的 EventBus 源码分析03 - EventBus 的注册(下篇)
这可能是最详细的 EventBus 源码分析04 - 事件的发送与执行
这可能是最详细的 EventBus 源码分析05 - Subscriber Index
上一章分析学习了 EventBus
对象的创建, 本章内容将对 EventBus
的注册源码进行分析.
由于代码较长, 所以分为上下两个章节.
- 上篇: 分析的是如何获取订阅者类中的订阅方法的.
- 下篇: 分析的是在获取到订阅者类中的所有订阅方法后如何进行订阅的流程.
简单来说本章的核心思想就是: 订阅者调用 EventBus.getDefault().register(this)
进行注册. EventBus
就会通过反射寻找订阅者内所有的订阅方法, 然后进行挨个遍历进行订阅. 如果是粘性 sticky
事件, 在订阅的时候就取出保存的 sticky
事件直接发送, 这样就做到了发布者先发布事件, 之后订阅者订阅事件, 接收订阅之前发布的粘性事件.
Ps: 上篇只分析是如何获取订阅者内所有的订阅方法.
本章涉及到的类及方法
|---EventBus.register()
| 1---SubscriberMethodFinder.findSubscriberMethods()
| 1.1---SubscriberMethodFinder.findUsingInfo()
| 1.1.1---SubscriberMethodFinder.prepareFindState()
| 1.1.2---FindState.initForSubscriber()
| 1.1.3---SubscriberMethodFinder.getSubscriberInfo()
| 1.1.4---SubscriberMethodFinder.findUsingReflectionInSingleClass()
| 1.1.5---FindState.moveToSuperclass()
| 1.1.6---SubscriberMethodFinder.getMethodsAndRelease()
| 2---EventBus.subscribe()
在下篇进行分析
在 EventBus 3.0
后的版本中增加了注解处理器, 在程序的编译时候, 就可以根据注解生成相对应的代码, 相对于之前的直接通过运行时反射, 大大的提高了程序的运行效率. 目前还是先使用最基本的运行时反射的方式来分析, 后面也会根据运行时反射的方式来分析.
EventBus.register()
public void register(Object subscriber) {
//通过反射获得订阅者的 Class 对象
Class<?> subscriberClass = subscriber.getClass();
//通过订阅者 Class 对象找到该对象内所有的订阅方法集合
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
//遍历并进行单个方法的订阅
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
在 register
方法中, 通过反射获得我们传入的 this
的 class
对象, 也就是订阅者的 class
对象. 然后调用了 subscriberMethodFinder.findSubscriberMethods
传入订阅者 class
对象后获得订阅对象内的所有订阅方法的集合. 这个是获得订阅方法集合的入口. 接着遍历所有的订阅方法挨个对其进行订阅..
1. SubscriberMethodFinder.findSubscriberMethods()
//是一个方法缓存池. key 为我们订阅者的 class 对象. value 为订阅者内所有的订阅方法
private static final Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>();
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
//分析 1
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
//分析 2
if (ignoreGeneratedIndex) {
//直接使用反射获取当前订阅者类的所有订阅方法
subscriberMethods = findUsingReflection(subscriberClass);
} else {
//先通过自动生成的自定义索引类 SubscriberIndex 方式获取,
//获取不到再通过反射的方式获取
subscriberMethods = findUsingInfo(subscriberClass);
}
if (subscriberMethods.isEmpty()) {
throw new EventBusException("Subscriber " + subscriberClass
+ " and its super classes have no public methods with the @Subscribe annotation");
} else {
//分析 3
METHOD_CACHE.put(subscriberClass, subscriberMethods);
return subscriberMethods;
}
}
-
分析 1
从方法缓存池中获取我们传入订阅者的class
对象的所有订阅方法集合. 若存在就直接返回.
首先看到根据我们传入的订阅者的class
对象从METHOD_CACHE
这个map
中获取一个SubscriberMethod
类型的列表.那么
METHOD_CACHE
是用来存放什么呢? 看一下它的声明.
private static final Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>()
METHOD_CACHE
是一个方法缓存池, 以订阅者的class
对象为key
, 以订阅者内部所有的订阅方法集合为value
为什么说
METHOD_CACHE
的value SubscriberMethod
是订阅者内部的所有订阅方法的集合呢?SubscriberMethod
就是将我们平时使用EventBus
过程中的那些参数基本上都封装起来了, 例如Method
方法.threadMode
线程模型,eventType
事件类型,priority
优先级,sticky
是否是粘性事件等等. -
分析 2
根具ignoreGeneratedIndex
的值来采用不同的方式获取订阅方法的集合ignoreGeneratedIndex
: 是否忽略注解器生成的索引类, 默认为false
. 可以通过EventBusBuilder
来设置它的值.Ps: 配置生成自定义索引类是
EventBus3.0
后新增的特性. 如何生成索引类以及他的使用,可以参考官方文档. 我们后面也会分析到. 分析 3
subscriberClass
就是订阅者的class
对象.
subscriberMethods
就是findUsingInfo(subscriberClass)
返回的当前订阅者的所有订阅方法集合.
将当前订阅者class
对象的所有订阅方法存储到METHOD_CACHE
中并返回subscriberMethods
按照默认的 ignoreGeneratedIndex = false
调用findUsingInfo(subscriberClass)
这个方法, 看它是如何拿到当前订阅者 class
对象内的所有订阅方法的.
1.1.SubscriberMethodFinder.findUsingInfo()
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
//从 findState 池中获取 findState 对象, 池中没有就创建一个新的对象
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
...
}
由于在这个方法内调用的方法较多, 所以会逐个分析.
先是通过调用 prepareFindState()
方法, 获得了一个 FindState
对象. 接着调用了 FindState.initForSubscriber
方法. 看名字大概能猜得出, 是做了一些初始化的操作.
那我们先来看 prepareFindState()
方法.
1.1.1.SubscriberMethodFinder.prepareFindState()
private static final int POOL_SIZE = 4;
private static final FindState[] FIND_STATE_POOL = new FindState[POOL_SIZE];
private FindState prepareFindState() {
synchronized (FIND_STATE_POOL) {
//长度为 4, 遍历 findState 对象池
for (int i = 0; i < POOL_SIZE; i++) {
FindState state = FIND_STATE_POOL[i];
//如果遍历到的对象非空,表示找到了可复用的 findState
if (state != null) {
//将该位置清空, 为了以后再来复用
FIND_STATE_POOL[i] = null;
//返回这个 findState
return state;
}
}
}
//如果遍历完都没有找到可用的 findState, 就创建一个.
return new FindState();
}
首先是初始化了一个长度为 4 的 FindState
类型数组 FIND_STATE_POOL
, 也可以叫它 FindState
对象池. 这个方法就是从 FindState
对象池中获取一个 FindState
对象返回, 如果对象池中没有找到可复用的那么就创建一个新的对象返回.
从 FIND_STATE_POOL
中取出不为空的FindState
对象, 然后将对应位置置空, 并返回FindState
对象, 这样可以避免多线程读写冲突以及读写不一致情况发生.
在 1.1 中获取了 FindState
对象后, 又调用了它的 initForSubscriber()
初始化方法. 现在跟进进入 FindState
看一下这是什么类.
1.1.2.FindState.initForSubscriber
static class FindState {
//用来保存所有的订阅方法信息
final List<SubscriberMethod> subscriberMethods = new ArrayList<>();
//以 Event 事件类型为 key, 订阅方法为 value
final Map<Class, Object> anyMethodByEventType = new HashMap<>();
//key 为方法名, Value 为订阅者 class 对象
final Map<String, Class> subscriberClassByMethodKey = new HashMap<>();
final StringBuilder methodKeyBuilder = new StringBuilder(128);
//subscriberClass 代表的是订阅者本身,也就是register函数的参数,自始至终不会变
// 会在遍历方法过程中变化, 比如目前在遍历父类方法,clazz 变量就指向父类
Class<?> subscriberClass;
Class<?> clazz;
//是否跳过父类. 即对父类也要查找, 默认为 false
boolean skipSuperClasses;
SubscriberInfo subscriberInfo;
//做赋值及清理操作
void initForSubscriber(Class<?> subscriberClass) {
this.subscriberClass = clazz = subscriberClass;
skipSuperClasses = false;
subscriberInfo = null;
}
//资源回收
void recycle() {
subscriberMethods.clear();
anyMethodByEventType.clear();
subscriberClassByMethodKey.clear();
methodKeyBuilder.setLength(0);
subscriberClass = null;
clazz = null;
skipSuperClasses = false;
subscriberInfo = null;
}
//判断订阅方法是否可以添加到 anyMethodByEventType 中
boolean checkAdd(Method method, Class<?> eventType) {
//通常,订阅者没有监听相同事件类型的方法。但是会有一种情况就是 子类订阅了当前事件, 它的父类也订阅了
//如果 key 没有重复,put 成功,则返回 null, 如果 key 重复了,返回的是被覆盖前的 valu 值.
Object existing = anyMethodByEventType.put(eventType, method);
if (existing == null) {
return true;
} else {
if (existing instanceof Method) {
...
anyMethodByEventType.put(eventType, this);
}
//再根据方法签名进行检查
return checkAddWithMethodSignature(method, eventType);
}
}
//主要目的还是为了, 不要出现一个订阅者有多个相同订阅方法订阅同一个事件. 如果有, 就将以前的 value 重新放进去覆盖掉新的.
private boolean checkAddWithMethodSignature(Method method, Class<?> eventType) {
...
//获得当前 Method 对象表示的方法的类的 Class 对象
Class<?> methodClass = method.getDeclaringClass();
//这里也会返回 null, 或者是覆盖前的 value 值
Class<?> methodClassOld = subscriberClassByMethodKey.put(methodKey, methodClass);
// 为 null 表示添加成功并且不是覆盖.
// 父类Class.isAssignableFrom(子类Class) 判断新添加的类是否是被覆盖类的子类.
if (methodClassOld == null || methodClassOld.isAssignableFrom(methodClass)) {
return true;
} else {
//如果不满足上述判断, 将被覆盖前的value, 重新放进去, 并返回 false.
subscriberClassByMethodKey.put(methodKey, methodClassOld);
return false;
}
}
//将成员变量clazz指向clazz的父类并排除系统类
void moveToSuperclass() {
if (skipSuperClasses) {
clazz = null;
} else {
clazz = clazz.getSuperclass();
String clazzName = clazz.getName();
if (clazzName.startsWith("java.") || clazzName.startsWith("javax.") ||clazzName.startsWith("android.") || clazzName.startsWith("androidx.")) {
//排除系统类
clazz = null;
}
}
}
}
FindState
类. 它是SubscriberMethodFinder
中的一个静态内部类. 内部存储了对订阅者class
对象一些列的集合, 并保存了一些订阅者的方法以及对订阅方法的校验.
在 FindState
中又出现了一个陌生的 SubscriberInfo
, 这是一个接口, 描述了一个订阅者应当具备的特性. 这是在使用自动生成的索引类后才会使用到的. 后面会对配置索引类的流程也进行分析. 这里先不管这些.
public interface SubscriberInfo {
Class<?> getSubscriberClass();//获取父类列表
SubscriberMethod[] getSubscriberMethods();//获取注册的订阅方法
SubscriberInfo getSuperSubscriberInfo();//获取父类(直接父类)的订阅信息
boolean shouldCheckSuperclass();//是否需要对父类进行检查
}
继续回到 1.1 findUsingInfo()
中向下分析
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
//从 findState 池中获取 findState 对象, 池中没有就创建一个新的对象
FindState findState = prepareFindState();
//传入订阅者的 Class 对象, 为 findState 做一些初始化工作
findState.initForSubscriber(subscriberClass);
//循环直到 findState 的 clazz 为空( clazz 为 java. 或 android. 或 javax. 等系统类则结束查找).
while (findState.clazz != null) {
//如果没有使用索引类的情况, 这里默认基本都是返回 null
findState.subscriberInfo = getSubscriberInfo(findState);
...
}
...
}
看到初始化完 FindState
后, 调用了 getSubscriberInfo()
方法将返回值赋值给 findState.subscriberInfo
. 不过在没有使用索引类的情况下, 这里返回的基本都是 null
. 跟进 getSubscriberInfo()
方法.
1.1.3.SubscriberMethodFinder.getSubscriberInfo(findState)
private SubscriberInfo getSubscriberInfo(FindState findState) {
//分析 1
if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) {
SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo();
if (findState.clazz == superclassInfo.getSubscriberClass()) {
return superclassInfo;
}
}
//分析 2
if (subscriberInfoIndexes != null) {
for (SubscriberInfoIndex index : subscriberInfoIndexes) {
SubscriberInfo info = index.getSubscriberInfo(findState.clazz);
if (info != null) {
return info;
}
}
}
return null;
}
分析 1
在FindState.initForSubscriber()
的方法中, 就已经将subscriberInfo
置为了null
. 所以第一个if
不会进入.分析 2
在没有使用索引类的情况下,subscriberInfoIndexes
是在SubscriberMethodFinder
的构造方法传入的, 默认为null
, 所以这里直接返回了null
.
如果配置了索引类, 就会进入到分析 2 的if
内.
这里只考虑没有使用索引类的情况. 所以直接返回 null
, 继续回到 1.1findUsingInfo ()
中
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
//从 findState 池中获取 findState 对象, 池中没有就创建一个新的对象
FindState findState = prepareFindState();
//传入订阅者的 Class 对象, 为 findState 做一些初始化工作
findState.initForSubscriber(subscriberClass);
//循环直到 findState 的 clazz 为空( clazz 为 java. 或 android. 或 javax. 等系统类则结束查找).
while (findState.clazz != null) {
//如果没有使用索引类的情况, 这里默认基本都是返回 null
findState.subscriberInfo = getSubscriberInfo(findState);
if (findState.subscriberInfo != null) {
...
} else {
//在运行时利用反射来获取所有被 @Subscribe 注解标注的订阅方法
findUsingReflectionInSingleClass(findState);
}
}
...
}
接着就进入到 else
中的 findUsingReflectionInSingleClass
方法, 开始利用反射来获取所有添加了@Subscribe
注解的的订阅方法.接着跟进去.
1.1.4.SubscriberMethodFinder.findUsingReflectionInSingleClass(findState)
private void findUsingReflectionInSingleClass(FindState findState) {
Method[] methods;
try {
//通过反射获取订阅者 class 对象内的所有方法.
methods = findState.clazz.getDeclaredMethods();
} catch (Throwable th) {
...
...
}
//对获取到的所有方法进行遍历
for (Method method : methods) {
//获取到方法的修饰符
int modifiers = method.getModifiers();
//判断修饰符是否是 public, 并且是否可以忽略
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
//获取方法的参数, 后面进行参数的判断, EventBus 只允许订阅事件中才参数只有一个
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length == 1) {
//判断这个方法是否有 @Subscribe 注解
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
if (subscribeAnnotation != null) {
//获得 Event 事件的 Class 对象.
Class<?> eventType = parameterTypes[0];
//检查是否需要保存到 findState 的 anyMethodByEventType 中, 返回 true 表示添加成功
if (findState.checkAdd(method, eventType)) {
//获得这个方法注解的 ThreadMode 线程模式, 根据不同的模式进行不同的线程调度
ThreadMode threadMode = subscribeAnnotation.threadMode();
// 往 findState 用来保存所有的订阅方法信息集合里面添加 SubscriberMethod (方法以及注解信息的封装类) ,
findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
}
}
} else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
//抛异常
}
} else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
//抛异常
}
}
}
这段代码的执行过程其实并不是很复杂. 在这里主要是使用了Java
的反射和对注解的解析.
- 首先通过反射来获取订阅者中所有的方法
- 遍历所有方法.
- 根据方法的类型, 参数和注解来找到真正的订阅方法.
- 找到订阅方法后调用
findState.checkAdd(method,eventType)
判断是否能将订阅方法相关信息保存到FindState.subscriberMethods
当中.
注: (findState.checkAdd
在上面 1.1.2 FindState
中有详细描述)
到这里已经完成对订阅者中所有订阅方法的查找. 接着再回到 1.1findUsingInfo()
中, 看到findUsingInfo()
的while
循环中还调用了一个方法, 就是FindState.moveToSuperclass()
.
1.1.5 FindState.moveToSuperclass()
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
//从 findState 池中获取 findState 对象, 池中没有就创建一个新的对象
FindState findState = prepareFindState();
//传入订阅者的 Class 对象, 为 findState 做一些初始化工作
findState.initForSubscriber(subscriberClass);
//循环直到 findState 的 clazz 为空( clazz 为 java. 或 android. 或 javax. 等系统类则结束查找).
while (findState.clazz != null) {
//如果没有使用索引类的情况, 这里默认基本都是返回 null
findState.subscriberInfo = getSubscriberInfo(findState);
if (findState.subscriberInfo != null) {
//获得编译期间订阅方法信息集合
SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
//接着遍历订阅方法.
for (SubscriberMethod subscriberMethod : array) {
//检测是否添加成功
if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
//将匹配的订阅方法添加到 findState 中保存所有订阅方法信息的集合中
findState.subscriberMethods.add(subscriberMethod);
}
}
} else {
//在运行时利用反射来获取所有被 @Subscribe 注解标注的订阅方法
findUsingReflectionInSingleClass(findState);
}
findState.moveToSuperclass();
}
//资源回收,并返回用来保存所有的订阅方法信息的集合(当前订阅者 class 对象中所有的订阅方法集合)
return getMethodsAndRelease(findState);
}
//将成员变量clazz指向clazz的父类并排除系统类
void moveToSuperclass() {
if (skipSuperClasses) {
clazz = null;
} else {
clazz = clazz.getSuperclass();
String clazzName = clazz.getName();
if (clazzName.startsWith("java.") || clazzName.startsWith("javax.") ||clazzName.startsWith("android.") || clazzName.startsWith("androidx.")) {
//排除系统类
clazz = null;
}
}
}
这里的逻辑就是 在第一次循环的时候, FindState.clazz
指向的就是当前订阅者类, 第一次循环结束的时候, 调用 moveToSuperclass()
如果当前订阅者有非系统类的父类, 就将 FindState.clazz
指向父类, 开始第二次循环, 接着查找父类中所有的订阅方法, 依次类推.
获取最后又调用了getMethodsAndRelease(findState)
方法并返回. 见名知意, 获取所有的方法并释放.
1.1.6 SubscriberMethodFinder. getMethodsAndRelease(FindState)
private List<SubscriberMethod> getMethodsAndRelease(FindState findState) {
//先获取 findState 中用来保存所有的订阅方法信息的集合
List<SubscriberMethod> subscriberMethods = new ArrayList<>(findState.subscriberMethods);
//回收重置 findState.
findState.recycle();
//重置 findState 对象池
synchronized (FIND_STATE_POOL) {
for (int i = 0; i < POOL_SIZE; i++) {
if (FIND_STATE_POOL[i] == null) {
FIND_STATE_POOL[i] = findState;
break;
}
}
}
return subscriberMethods;
}
getMethodsAndRelease
就先获取FindState.subscriberMethods
, 接着将FindState
中的内容及数据重置, 最后重置FindState
对象池.以便复用. 最后返回 subscriberMethods
.
至此, EventBus.getDefault().register(this)
注册方法的第一步获取订阅者内所有的订阅方法就分析完了, 下一章将会遍历这些方法, 依次对其进行订阅.