EventBus3.x源码分析之注册(二)
上篇文章分析了通用的订阅方法注册,其实EventBus3.0以后提供了一种更加高效的注册方式;
主要思路:
就是配合meta包中提供的SubscriberInfo接口,通过EventBusAnnotationProcessor编译时生成所需的订阅信息,用者只需要在EventBus的构建类EventBusBuilder中通过addIndex添加即可完成订阅。
配置依赖
官方文档:http://greenrobot.org/eventbus/documentation/subscriber-index/
How to generate the index
Gradle中配置如下代码:
android {
defaultConfig {
javaCompileOptions {
annotationProcessorOptions {
//这里可以指定编译时临时生成的SubscriberInfoIndex实例
//通过编译时生成的订阅信息是被放到EventBusDemo\app\build\generated\source\apt\debug\com\yiche\eventbusdemo\MyEventBusIndex.java中的
arguments = [ eventBusIndex : 'com.yiche.eventbusdemo.MyEventBusIndex' ]
}
}
}
}
dependencies {
implementation 'org.greenrobot:eventbus:3.1.1'
annotationProcessor 'org.greenrobot:eventbus-annotation-processor:3.1.1'
}
自动生成订阅信息代码的类为org.greenrobot.eventbus.annotationprocessor.EventBusAnnotationProcessor,本人也只是了解部分原理,就不做分析了。
通过这个类会将方法中带有@Subscribe注解,public,非static等等一系列筛选后将订阅方法自动生成到MyEventBusIndex中:
/** This class is generated by EventBus, do not edit. */
//以下便是自动生成的SubscriberInfoIndex实例
public class MyEventBusIndex implements SubscriberInfoIndex {
private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX;
static {
SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>();
putIndex(new SimpleSubscriberInfo(MainActivity.class, true, new SubscriberMethodInfo[] {
new SubscriberMethodInfo("onEventComming", String.class, ThreadMode.POSTING, 2, false),
new SubscriberMethodInfo("onEventTest", String.class, ThreadMode.POSTING, 1, false),
}));
}
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;
}
}
}
在编译时以一种固定格式生成该类,静态代码块中会最先将订阅类包括订阅方法封装好,通过getSubscriberInfo(Class
SubscriberMethodFinder#
private static final Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>();
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
//是否忽略注册时通过addIndex方法添加的订阅信息(属于性能优化)
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 {
//以订阅类名为key,订阅类中的订阅方法为value存入订阅方法的缓存容器(同步安全效率高的Hasmap,采用分段锁技术)中。
METHOD_CACHE.put(subscriberClass, subscriberMethods);
return subscriberMethods;
}
}
其实ignoreGeneratedIndex默认是false就是希望通过这种方式也添加订阅方法,会走到
SubscriberMethodFinder#
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
//在当前类和自定义的父类中找不到订阅方法了为止
while (findState.clazz != null) {
findState.subscriberInfo = getSubscriberInfo(findState);
if (findState.subscriberInfo != null) {
//从通过addIndex方法,添加的订阅信息找到并添加
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);
}
在获取findState.subscriberInfo = getSubscriberInfo(findState);的时候
private SubscriberInfo getSubscriberInfo(FindState findState) {
//getSuperSubscriberInfo()会一直为null,但可以重写AbstractSubscriberInfo的实现类,在super中传入SubscriberInfo的实例即可
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;
}
这里找到编译生成订阅信息的关键方法!
会遍历subscriberInfoIndexes这个list,这个list就是在EventBusBuilder的addIndex方法添加的,添加以后就会执行这个if语句代码块,找到订阅信息并返回。
区别:
1、正常情况注册时在运行时要筛选方法(修饰符,参数个数,是否带有注解),然后进行方法去重check,运行时添加到订阅列表
2、而这里面的一系列订阅信息都是通过编译生成的,只需要进行是否为重复方法的check校验,因为这些都在编译时完成了,运行时只需要取出订阅即可,大大提升了性能。
当然也可以自己手动创建SubscriberInfo实例,官方也在测试代码中给出了例子。也可以根据分析这些代码,梳理出来自动生成的代码时如何将那些方法编译时生成订阅信息的。
测试代码块中的:EventBusIndexTest#testManualIndexWithoutAnnotation
public class EventBusIndexTest {
private String value;
/** Ensures the index is actually used and no reflection fall-back kicks in. */
@Test
public void testManualIndexWithoutAnnotation() {
SubscriberInfoIndex index = new SubscriberInfoIndex() {
@Override
public SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) {
Assert.assertEquals(EventBusIndexTest.class, subscriberClass);
SubscriberMethodInfo[] methodInfos = {
new SubscriberMethodInfo("someMethodWithoutAnnotation", String.class)
};
return new SimpleSubscriberInfo(EventBusIndexTest.class, false, methodInfos);
}
};
EventBus eventBus = EventBus.builder().addIndex(index).build();
eventBus.register(this);
eventBus.post("Yepp");
eventBus.unregister(this);
Assert.assertEquals("Yepp", value);
}
public void someMethodWithoutAnnotation(String value) {
this.value = value;
}
}
EventBusBuilder将通过addIndex方法传入了的实现类。
在通过getSubscriberInfo(FindState findState)中,会在SubscribeInfo实现类中查找一系列的订阅方法信息。
实现类如下:
public class SimpleSubscriberInfo extends AbstractSubscriberInfo {
private final SubscriberMethodInfo[] methodInfos;
public SimpleSubscriberInfo(Class subscriberClass, boolean shouldCheckSuperclass, SubscriberMethodInfo[] methodInfos) {
super(subscriberClass, null, shouldCheckSuperclass);
this.methodInfos = methodInfos;
}
@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;
}
}
这里在index.getSubscriMethods()时,通过指定的方法名和方法类型快速的找到需要订阅的方法
public abstract class AbstractSubscriberInfo implements SubscriberInfo {
private final Class subscriberClass;
private final Class<? extends SubscriberInfo> superSubscriberInfoClass;
private final boolean shouldCheckSuperclass;
protected AbstractSubscriberInfo(Class subscriberClass, Class<? extends SubscriberInfo> superSubscriberInfoClass,
boolean shouldCheckSuperclass) {
this.subscriberClass = subscriberClass;
this.superSubscriberInfoClass = superSubscriberInfoClass;
this.shouldCheckSuperclass = shouldCheckSuperclass;
}
...
protected SubscriberMethod createSubscriberMethod(String methodName, Class<?> eventType, ThreadMode threadMode,
int priority, boolean sticky) {
try {
Method method = subscriberClass.getDeclaredMethod(methodName, eventType);
return new SubscriberMethod(method, eventType, threadMode, priority, sticky);
} catch (NoSuchMethodException e) {
throw new EventBusException("Could not find subscriber method in " + subscriberClass +
". Maybe a missing ProGuard rule?", e);
}
}
}
将需要订阅的方法及参数类型先封装好,传入这个实例,通过getSubscriberMethods()在将这些方法找出来通过父类的createSubscriberMethod方法封装到SubscriberMethod中。
一般手动创建这种方式也不会被使用到。
总结:
addIndex(SubscriberInfo info)这种方式就是找到程序中带有@Subscribe注解的方法,生成subscribtioninfo实例时会检验你写的方法的合法性,一旦通过,它会对此方法信任并生成那个实例,在运行时需要订阅的时候直接通过方法名和参数类型进行获取。这就是提高性能的关键。