#|EventBus中SubscriberInfoIndex的使用

前言 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的源码的一点感悟,来谈谈注解处理器中SubscriberInfoIndexEventBus3.0的使用
SubscriberInfoIndex 【#|EventBus中SubscriberInfoIndex的使用】EventBus的注册和发送事件的源码执行过程就不再这里讲了,这篇文章就讲得蛮清楚的Android开源框架源码鉴赏:EventBus
一般获取它的实例就是通过EventBus.getDefault()获取,看源码很明显就是一个静态单例的实现,但是我要说的是它的构造方法就是public的,说明外界就可以直接通过new EventBus获取其实例,而它的构造方法有一个
EventBus(EventBusBuilder builder) { .... }

这样的重载,这里我们就重点关注下 EventBusBuilder,看到它其实就可以联想到EventBus类可以通过Builder构建者模式获取其实例
#|EventBus中SubscriberInfoIndex的使用
文章图片

从这里就可以证明。
List 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这个变量就封装了注册方法的信息。看到这里,再来看官方提供给我们的例子
  • 1.) 官方Demo
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 = https://www.it610.com/article/value; } }

Demo首先实例化了一个SubscriberInfoIndex匿名内部类,并且实现了getSubscriberInfo方法。在这个方法中创建了一个SubscriberMethodInfo数组,SubscriberMethodInfo封装了方法名,当前线程,参数的事件类型,优先级,是否是粘性事件,然后将SubscriberMethodInfo数组封装到SimpleSubscriberInfo中,参数名分别为当前类名的class, 是否应该检查父类,SubscriberMethodInfo数组,SimpleSubscriberInfoSubscriberInfo的实现类,最后将SimpleSubscriberInfo的实例化结果作为getSubscriberInfo的返回值。
到这里就构成了一个index,然后就去通过构建者模式获取EventBus的实例,剩下用法就和之前的一样的
1.index在EventBus内部其实就是添加到了subscriberInfoIndexes集合上,前面我们也看到在EventBus内部会判断subscriberInfoIndexes是否为null, 不为null直接获取里面封装的方法信息。这样就可以避免反射的影响。
2.我们也可以不必使用@Subscribe注解了
  • 2.) 注解处理器中的SubscriberInfoIndex
EventBus3.0增加了注解处理器,关于注解处理器我看了这篇文章比较详细。看完之后再来看EventBus的注解处理器这个模块应该有帮助。有个地方需要提下的就是下图
#|EventBus中SubscriberInfoIndex的使用
文章图片

我们需要在build文件中配置OPTION_EVENT_BUS_INDEX,不然后面的不会执行,至于怎么配,看官方的一个demo
#|EventBus中SubscriberInfoIndex的使用
文章图片

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级别的使用上,对于一些开源框架更深入的基本的原理还是需要掌握的,这样可以更好的使用它们,而不总是写些模板代码,学会了这些框架的设计思想,将它们运用到我们的项目中还是很有帮助的。

    推荐阅读