EventBus使用起来还是很方便的,它的源码是基于反射实现的,在3.0之后增加了注解处理器,在程序的编译时候,就可以根据注解生成相对应的代码,相对于之前的直接通过运行时反射,大大提高了程序的运行效率,但是在3.0默认的还是通过反射去查找用@Subscribe
标注的方法,一般在使用的时候基本都是这个模式
//注册
if (!EventBus.getDefault().isRegistered(this)) {
EventBus.getDefault().register(this)
}
//反注册
if (EventBus.getDefault().isRegistered(this)) {
EventBus.getDefault().unregister(this)
}
虽然用起来还是很方便的,但是这种默认就是通过反射去查找在代码中注册的方法,这就在性能上有一定的损耗。所以通过这些时读EventBus3.0
的源码的一点感悟,来谈谈注解处理器中SubscriberInfoIndex
在EventBus3.0
的使用
EventBus
的注册和发送事件的源码执行过程就不再这里讲了,这篇文章就讲得蛮清楚的Android开源框架源码鉴赏:EventBus
一般获取它的实例就是通过EventBus.getDefault()
获取,看源码很明显就是一个静态单例的实现,但是我要说的是它的构造方法就是public
的,说明外界就可以直接通过new EventBus
获取其实例,而它的构造方法有一个
EventBus(EventBusBuilder builder) {
....
}
这样的重载,这里我们就重点关注下 EventBusBuilder
,看到它其实就可以联想到EventBus
类可以通过Builder
构建者模式获取其实例
从这里就可以证明。
List<SubscriberInfoIndex> subscriberInfoIndexes;
在EventBusBuilder
中有这样一个变量,通过这个变量可以去使用注解处理器生成的代码。SubscriberInfoIndex
就是一个接口,而注解生成器生成的类也是继承的它,我们也可以自己去继承它,定制自己的需求,不需要反射的EventBus。
在EventBus
的源码中有这样一段代码
//SubscriberMethodFinder
List findSubscriberMethods(Class subscriberClass) {
....
//ignoreGeneratedIndex默认为false,我们可以设置为true,忽略SubscriberInfoIndex的代码
if (ignoreGeneratedIndex/*false*/) {
subscriberMethods = findUsingReflection(subscriberClass);
} else {
subscriberMethods = findUsingInfo(subscriberClass); //走这一步
}
...
}
private List findUsingInfo(Class subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
findState.subscriberInfo = getSubscriberInfo(findState); //默认为null
...
}
return getMethodsAndRelease(findState);
}
private SubscriberInfo getSubscriberInfo(FindState findState) {
...
if (subscriberInfoIndexes != null) {
for (SubscriberInfoIndex index : subscriberInfoIndexes) {
SubscriberInfo info = index.getSubscriberInfo(findState.clazz);
if (info != null) {
return info;
}
}
}
return null;
}
如果subscriberInfoIndexes不为null,就会去调用SubscriberInfoIndex的getSubscriberInfo方法获取SubscriberInfo,SubscriberInfo这个变量就封装了注册方法的信息。看到这里,再来看官方提供给我们的例子
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;
}
}
Demo首先实例化了一个SubscriberInfoIndex
匿名内部类,并且实现了getSubscriberInfo
方法。在这个方法中创建了一个SubscriberMethodInfo
数组,SubscriberMethodInfo
封装了方法名,当前线程,参数的事件类型,优先级,是否是粘性事件,然后将SubscriberMethodInfo
数组封装到SimpleSubscriberInfo
中,参数名分别为当前类名的class
, 是否应该检查父类,SubscriberMethodInfo
数组,SimpleSubscriberInfo
是SubscriberInfo
的实现类,最后将SimpleSubscriberInfo
的实例化结果作为getSubscriberInfo
的返回值。
到这里就构成了一个index
,然后就去通过构建者模式获取EventBus
的实例,剩下用法就和之前的一样的
1.index在EventBus内部其实就是添加到了
subscriberInfoIndexes
集合上,前面我们也看到在EventBus
内部会判断subscriberInfoIndexes
是否为null, 不为null直接获取里面封装的方法信息。这样就可以避免反射的影响。
2.我们也可以不必使用@Subscribe注解了
EventBus3.0
增加了注解处理器,关于注解处理器我看了这篇文章比较详细。看完之后再来看EventBus
的注解处理器这个模块应该有帮助。有个地方需要提下的就是下图
我们需要在build文件中配置OPTION_EVENT_BUS_INDEX
,不然后面的不会执行,至于怎么配,看官方的一个demo
javaCompileOptions {
annotationProcessorOptions {
arguments = [ eventBusIndex : 'org.greenrobot.eventbus.EventBusTestsIndex' ]
}
}
模板代码直接复制就可以了。看下生成的类文件
/** This class is generated by EventBus, do not edit. */
public class EventBusTestsIndex implements SubscriberInfoIndex {
private static final Map, SubscriberInfo> SUBSCRIBER_INDEX;
static {
SUBSCRIBER_INDEX = new HashMap, SubscriberInfo>();
putIndex(new SimpleSubscriberInfo(EventBusAndroidOrderTest.class, true, new SubscriberMethodInfo[] {
new SubscriberMethodInfo("onEvent", String.class, ThreadMode.MAIN),
new SubscriberMethodInfo("onEvent", EventBusAndroidOrderTest.OrderedEvent.class, ThreadMode.MAIN_ORDERED),
}));
....
}
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;
}
}
}
基本就是这种模式,然后我们就可以通过EventBus.builder().addIndex(index).build()
这种形式去获取EventBus
的实例,这样就可以避免大量反射在EventBus
中使用造成的性能损耗。
到了这里基本就完了,可以看到注解处理器还是很有用的,无论在EventBus
,还是ButterKnife
中都有广泛的使用,所以注解处理器这方面还是要多学习下的。另外,我们不能只是简单停留在Api
级别的使用上,对于一些开源框架更深入的基本的原理还是需要掌握的,这样可以更好的使用它们,而不总是写些模板代码,学会了这些框架的设计思想,将它们运用到我们的项目中还是很有帮助的。