前一篇文章 Android EventBus使用理解 最后就在怀疑反射查找订阅方法会影响效率,就在官网上看到了推荐使用Subscriber Index在APP产品中。给的理由是更快和避免崩溃。
使用Subscriber Index避免用反射在运行时做昂贵的订阅方法的查找。它用注解处理器在编译时查找订阅方法。
真的是很赞!在运行时的花销给放在编译时完成了。
满足要求
- 订阅方法和订阅者类必须是public
- 事件类必须是public
- 订阅方法不能放到匿名类里面
如何生成index
如果是Java语言,使用注解处理器。如果使用Kotlin,则使用Kapt
这里主要讲使用Java的配置方式,由于我这个build.gradle.kts是用的Kotlin脚本,我贴出来我的配置
android {
defaultConfig {
javaCompileOptions {
annotationProcessorOptions.arguments["eventBusIndex"] = "com.example.testeventbus.MyEventBusIndex"
}
}
}
dependencies {
val eventbus_version = "3.3.1"
implementation("org.greenrobot:eventbus:$eventbus_version")
annotationProcessor("org.greenrobot:eventbus-annotation-processor:$eventbus_version")
}
如何使用index
首先需要构建一下项目。像我配置的,它会自动生成com.example.testeventbus.MyEventBusIndex类。
在代码中初始化的地方,在调用EventBus.getDefault()之前,需要调用EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus()。
这样就可以了,其他的啥都不用动了。
结合代码分析一下index
还是在之前那篇文章 Android EventBus使用理解 的那个例子的基础上分析一下。
主要是分析一下现在查询订阅方法那块的逻辑。
增加的类
使用注解自动生成的类是com.example.testeventbus.MyEventBusIndex,看一下它的实现:
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("onMessageEvent", MessageEvent.class, ThreadMode.MAIN),
new SubscriberMethodInfo("onMessage", MessageEvent.class, ThreadMode.MAIN),
}));
}
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;
}
}
}
该类继承了SubscriberInfoIndex ,实现了getSubscriberInfo(Class<?> subscriberClass)方法,同时我们看到在MyEventBusIndex 类的静态代码块中,将订阅方法的相关信息放入静态成员SUBSCRIBER_INDEX中。
在这个例子中,SUBSCRIBER_INDEX的key为MainActivity.class,MainActivity则是订阅者对象。SUBSCRIBER_INDEX其实就是以订阅者类对象的Class作为key,值就是SubscriberInfo 实现类。在这,它的值是SimpleSubscriberInfo类对象。
SimpleSubscriberInfo初始化的第三个参数是一个SubscriberMethodInfo数组,SubscriberMethodInfo是用来描述订阅方法的。例子中,我们声明了两个方法,所以这里会有两个数组。Android EventBus使用理解 里我们通过反射查找的就是这两个方法,现在直接就已经找到了这两个方法,这就是官网上说的将运行时查找,放在编译时查找的意思。
将生成的Index放进EventBus.getDefault()
因为我们每次调用都是通过EventBus.getDefault()来实现相关的操作,所以我们需要把生成的index放进EventBus.getDefault()中它才能起作用。它就是通过EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus()来完成的。
先看EventBus.builder().addIndex(new MyEventBusIndex())
/** Adds an index generated by EventBus' annotation preprocessor. */
public EventBusBuilder addIndex(SubscriberInfoIndex index) {
if (subscriberInfoIndexes == null) {
subscriberInfoIndexes = new ArrayList<>();
}
subscriberInfoIndexes.add(index);
return this;
}
可见,这是将新生成的MyEventBusIndex添加进成员变量subscriberInfoIndexes中了。subscriberInfoIndexes是ArrayList类型。
再看installDefaultEventBus()方法:
/**
* Installs the default EventBus returned by {@link EventBus#getDefault()} using this builders' values. Must be
* done only once before the first usage of the default EventBus.
*
* @throws EventBusException if there's already a default EventBus instance in place
*/
public EventBus installDefaultEventBus() {
synchronized (EventBus.class) {
if (EventBus.defaultInstance != null) {
throw new EventBusException("Default instance already exists." +
" It may be only set once before it's used the first time to ensure consistent behavior.");
}
EventBus.defaultInstance = build();
return EventBus.defaultInstance;
}
}
/** Builds an EventBus based on the current configuration. */
public EventBus build() {
return new EventBus(this);
}
直接就build()生成EventBus.defaultInstance了。以后再调用EventBus.getDefault()就是得到EventBus.defaultInstance了。
EventBus初始化关于index的代码:
EventBus(EventBusBuilder builder) {
logger = builder.getLogger();
subscriptionsByEventType = new HashMap<>();
typesBySubscriber = new HashMap<>();
stickyEvents = new ConcurrentHashMap<>();
mainThreadSupport = builder.getMainThreadSupport();
mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
backgroundPoster = new BackgroundPoster(this);
asyncPoster = new AsyncPoster(this);
indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
builder.strictMethodVerification, builder.ignoreGeneratedIndex);
logSubscriberExceptions = builder.logSubscriberExceptions;
logNoSubscriberMessages = builder.logNoSubscriberMessages;
sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
throwSubscriberException = builder.throwSubscriberException;
eventInheritance = builder.eventInheritance;
executorService = builder.executorService;
}
主要就是中间两句关于indexCount 和subscriberMethodFinder 的了。将Index集合设置到SubscriberMethodFinder类对象subscriberMethodFinder 之中。我们知道SubscriberMethodFinder是关于查找订阅方法的,查找时,是和subscriberInfoIndexes有关的,之前没有设置Index时,它都是空的,都跳过了。现在设置了值了,就要重新看一下那块查找订阅方法的逻辑了。
查找订阅方法
我们不用重头开始看,直接跳到相关代码,就在subscriberMethodFinder 类的findUsingInfo(Class<?> subscriberClass)方法中:
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(findState)这块,之前这块返回的findState.subscriberInfo为空,所以会走findUsingReflectionInSingleClass(findState),现在它返回不为空了,所以不用在走反射的查找方式了。
现在就看一下getSubscriberInfo(findState):
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;
}
这个时候,findState.subscriberInfo为null,但是subscriberInfoIndexes 不为null了,因为我们赋值了。我们的例子来说,subscriberInfoIndexes里面有一个MyEventBusIndex对象。然后调用的就是它的getSubscriberInfo(Class<?> subscriberClass)方法。它返回的是一个SimpleSubscriberInfo对象。接着就在该对象返回了。
返回到findUsingInfo()方法了,接着会调用SimpleSubscriberInfo的getSubscriberMethods()。如下:
@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;
}
SimpleSubscriberInfo里面现在有2个SubscriberMethodInfo ,现在需要将SubscriberMethodInfo 转化为SubscriberMethod,是调用的createSubscriberMethod()实现的。
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);
}
}
subscriberClass就订阅者类对象的Class,这里通过getDeclaredMethod()得到对应的Method,这样就能生成SubscriberMethod对象了。
这样返回到findUsingInfo()方法里,就又开始通过findState.checkAdd()来判断是否能将订阅方法添加到结果集合中了,这个判断逻辑就在前一篇文章 Android EventBus使用理解 里了。
现在理解了Index怎么替代通过反射查找订阅方法了吧。