一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第22天,点击查看活动详情。
EventBus.register()方法中通过findSubscriberMethods()方法获取了订阅者的所有的订阅方法,findSubscriberMethods是SubscriberMethodFinder类中的方法,SubscriberMethodFinder类由EventBus类创建,构造方法如下
SubscriberMethodFinder(
List<SubscriberInfoIndex> subscriberInfoIndexes,
boolean strictMethodVerification,
boolean ignoreGeneratedIndex) {
}
- subscriberInfoIndexes 注解生成的索引
- strictMethodVerification 反射获取订阅方法的时候遇到非法情况是否抛出异常
- ignoreGeneratedIndex 是否忽略索引,强制使用反射获取订阅信息
查找订阅方法
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
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;
}
}
EventBus3.x提供了两种途径查找订阅方法,一个是运行时反射,一个是注解处理器,我们可以通过设置ignoreGeneratedIndex决定使用那种,由源码可以看出,如果没有找到订阅方法就会抛出异常, 所以订阅类或者其父类必须提供一个公开的被@Subscribe注解修饰的方法。
无论使用哪种途径获取订阅方法,都是一个耗时过程m所以EventBus提供了缓存池METHOD_CACHE,如果池中已经存在当前订阅者的信息,则直接返回,否则获取了之后放入池中。
运行时反射
通过反射获取订阅方法
private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
findUsingReflectionInSingleClass(findState);
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}
FindState类是订阅方法的一个临时容器,它包含了订阅类和其订阅方法等临时信息。由于使用比较频繁,性能开销比较大,所以使用了数据池优化。
private FindState prepareFindState() {
synchronized (FIND_STATE_POOL) {
for (int i = 0; i < POOL_SIZE; i++) {
FindState state = FIND_STATE_POOL[i];
if (state != null) {
FIND_STATE_POOL[i] = null;
return state;
}
}
}
return new FindState();
}
EventBus提供了一个大小为POOL_SIZE(4)的FindState池(FIND_STATE_POOL),优先从池中获取,没有则直接创建。获取完成之后调用initForSubscriber初始化订阅者的信息。 然后通过findUsingReflectionInSingleClass()查找订阅方法。
private void findUsingReflectionInSingleClass(FindState findState) {
Method[] methods;
try {
methods = findState.clazz.getDeclaredMethods();
} catch (Throwable th) {
try {
methods = findState.clazz.getMethods();
} catch (LinkageError error) { // super class of NoClassDefFoundError to be a bit more broad...
//省略异常日志
}
findState.skipSuperClasses = true;
}
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) {
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
if (subscribeAnnotation != null) {
Class<?> eventType = parameterTypes[0];
if (findState.checkAdd(method, eventType)) {
ThreadMode threadMode = subscribeAnnotation.threadMode();
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");
}
}
}
这里使用了getDeclaredMethods()和getMethods()两种方法获取Methods数组,他们的区别如下:
- getDeclaredMethods() 获取当前类非继承的所有方法
- getMethods() 获取当前类以及父类,接口的所有public方法
由于getDeclaredMethods()的效率更高,所以优先使用它获取,如果失败了再通过getMethods()获取,而getMethods()本身就会获取父类以及接口的public方法,所以在获取成功之后就没必要再去父类查找了,所以设置findState.skipSuperClasses = true。
查找到Method数组之后,筛选这些Method,获取我们需要的
- (modifiers & Modifier.PUBLIC) != 0 方法必须是公开的
- (modifiers & MODIFIERS_IGNORE) == 0 方法必须是非静态的,非抽象的,以及不是编译过程中添加的
- parameterTypes.length == 1 方法必须只有一个参数
- subscribeAnnotation != null 方法必须有@Subscribe注解
满足以上条件的Method不一定是我们需要的结果,还需要通过checkAdd()方法检查下
boolean checkAdd(Method method, Class<?> eventType) {
Object existing = anyMethodByEventType.put(eventType, method);
if (existing == null) {
return true;
} else {
if (existing instanceof Method) {
if (!checkAddWithMethodSignature((Method) existing, eventType)) {
throw new IllegalStateException();
}
anyMethodByEventType.put(eventType, this);
}
return checkAddWithMethodSignature(method, eventType);
}
}
EventBus首先通过事件类型eventType在anyMethodByEventType中查找当前类是否添加了对eventType类型处理的方法,如果没有,则认为是有效的订阅方法,否则通过checkAddWithMethodSignature()方法检测方法的签名
private boolean checkAddWithMethodSignature(Method method, Class<?> eventType) {
methodKeyBuilder.setLength(0);
methodKeyBuilder.append(method.getName());
methodKeyBuilder.append('>').append(eventType.getName());
String methodKey = methodKeyBuilder.toString();
Class<?> methodClass = method.getDeclaringClass();
Class<?> methodClassOld = subscriberClassByMethodKey.put(methodKey, methodClass);
if (methodClassOld == null || methodClassOld.isAssignableFrom(methodClass)) {
return true;
} else {
subscriberClassByMethodKey.put(methodKey, methodClassOld);
return false;
}
}
这里计算方法的特征签名比较简单,直接用“methodName>eventTypeName”拼接,如果该签名的方法没有出现过(methodClassOld == null),则认为是有效的订阅方法,如果出现了,就判断已经添加过的订阅方法所在的类(methodClassOld)是否为当前订阅方法所在类(methodClass)的父类或者接口(methodClassOld.isAssignableFrom(methodClass))。如果是,说明子类重写了该方法,也认为是有效的,否则返回false并记录。
找到合法的Method之后,收集信息,添加到容器findState当中,如果找到非法的Method,根据strictMethodVerification和其他参数决定是否抛出异常。
当前类的订阅方法查找完成之后,还需要查找父类,通过moveToSuperclass改变查找的class
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;
}
}
}
这里有两点细节优化了查找效率
- 上面提到使用getMethods()本身就会获取父类以及接口的public方法,然后设置skipSuperClasses = true。所以这里一开始就判断skipSuperClasses,如果为true就设置clazz=null
- 跳过系统类,这些类没必要解析
查找完成之后,获取到的信息都保存在了FindState中,通过getMethodsAndRelease()获取订阅信息
private List<SubscriberMethod> getMethodsAndRelease(FindState findState) {
List<SubscriberMethod> subscriberMethods = new ArrayList<>(findState.subscriberMethods);
findState.recycle();
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做了两件事
- 返回订阅方法subscriberMethods
- 回收findState,先清空,然后放到FIND_STATE_POOL缓存中,至此FindState有取有放,形成了闭环
注解处理器
通过注解处理器获取订阅方法
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
findState.subscriberInfo = getSubscriberInfo(findState);
if (findState.subscriberInfo != null) {
SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
for (SubscriberMethod subscriberMethod : array) {
if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
findState.subscriberMethods.add(subscriberMethod);
}
}
} else {
findUsingReflectionInSingleClass(findState);
}
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}
可以看到,通过注解处理器获取订阅方法流程与反射获取大体一致,先使用getSubscriberInfo()查找订阅信息,如果查找为null,降级使用反射查找
private SubscriberInfo getSubscriberInfo(FindState findState) {
if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) {
SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo();
if (findState.clazz == superclassInfo.getSubscriberClass()) {
return superclassInfo;
}
}
if (subscriberInfoIndexes != null) {
for (SubscriberInfoIndex index : subscriberInfoIndexes) {
SubscriberInfo info = index.getSubscriberInfo(findState.clazz);
if (info != null) {
return info;
}
}
}
return null;
}
第一次进入getSubscriberInfo()方法findState.subscriberInfo是空的,所以先从subscriberInfoIndexes中查找SubscriberInfo,查找完当前类之后,通过moveToSuperclass(),改变findState.clazz然后继续查找父类的SubscriberInfo,关于subscriberInfoIndexes,他是通过EventBusBuilder的addIndex()方法添加的,至于如何添加,这涉及到对 @Subscribe 注解的解析,将在后面EventBusAnnotationProcessor中提到。
经过上面两种途径获取到List后将回到EventBus类由subscribe()方法继续处理。