前言
EvenBus 2.x时代的消息订阅只能使用固定的方法名,比如:
onEvent
onEventMainThread
onEventBackgroundThread
onEventAsync
从EventBus 3.0开始支持使用注解来定义消息订阅者(subscriber),方法名是随意的,比如:
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {
// Do something
}
不过,EventBus3.0还有一个鲜为人知的功能,而且是一个可选的功能,默认是不带的,需要手动添加支持。
那就是使用APT在编译期间搜集所有的SubscribMethod。默认是在运行期间通过反射去搜集SubscriberMethod,这样性能就差点。
EventBus添加APT支持
在EventBus的github主页上有一个不起眼的地方,有提到建议使用APT(只是建议不是必须使用):
点击这个链接会跳转到单独介绍如何使用subscriber index(APT生成的代码)的指南页面:
https://greenrobot.org/eventbus/documentation/subscriber-index/
下面通过实践介绍如何使用subscriber index来提升EventBus的性能。
关于EventBus集成subscriber index很简单,先通过kapt额外添加eventbus-annotation-processor
依赖:
dependencies {
implementation "org.greenrobot:eventbus:3.2.0"
kapt "org.greenrobot:eventbus-annotation-processor:3.2.0"
}
然后给APT添加参数,注明生成subscriber index类的路径:
defaultConfig {
kapt {
arguments {
arg('eventBusIndex', 'com.devnn.bean.AppEventBusIndex')
}
}
}
这个类是自动生成的,不需要手动去创建,命名可以随意。
build项目之后就会在build目录生成Java类:com.devnn.bean.AppEventBusIndex.java
然后需要在app启动时将这个类注册进EventBus中:
class MyApp :Application(){
override fun onCreate() {
super.onCreate()
EventBus.builder().addIndex(AppEventBusIndex()).build()
}
}
其它像以前一样正常使用EventBus即可。
关于APT有不了解的可以看笔者另一篇文章:
Google AutoService的使用与源码解析
下面分析这个类的作用。
EventBus APT流程分析
EventBus是通过建造者模式构建的,在EventBusBuilder类通过addIndex方法接收SubscriberInfoIndex对象:
/** Adds an index generated by EventBus' annotation preprocessor. */
public EventBusBuilder addIndex(SubscriberInfoIndex index) {
if (subscriberInfoIndexes == null) {
subscriberInfoIndexes = new ArrayList<>();
}
subscriberInfoIndexes.add(index);
return this;
}
而SubscriberInfoIndex是一个接口:
package org.greenrobot.eventbus.meta;
/**
* Interface for generated indexes.
*/
public interface SubscriberInfoIndex {
SubscriberInfo getSubscriberInfo(Class<?> subscriberClass);
}
EventBus源码中并没有任何关于这个接口的实现类,这个实现类就是需要通过我们去实现的。当然是不需要手动写代码去实现,而且通过apt自动帮我们生成。
在上述集成eventbus-annotation-processor
步骤完成之后,build项目就会在build目录生成com.devnn.bean.AppEventBusIndex.java
,它就是SubscriberInfoIndex接口的实现。
AppEventBusIndex完整代码如下:
package com.devnn.bean;
import org.greenrobot.eventbus.meta.SimpleSubscriberInfo;
import org.greenrobot.eventbus.meta.SubscriberMethodInfo;
import org.greenrobot.eventbus.meta.SubscriberInfo;
import org.greenrobot.eventbus.meta.SubscriberInfoIndex;
import org.greenrobot.eventbus.ThreadMode;
import java.util.HashMap;
import java.util.Map;
/** This class is generated by EventBus, do not edit. */
public class AppEventBusIndex implements SubscriberInfoIndex {
private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX;
static {
SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>();
putIndex(new SimpleSubscriberInfo(com.devnn.demo.MainActivity.class, true, new SubscriberMethodInfo[] {
new SubscriberMethodInfo("onReceiveEvent", com.devnn.demo.MyEvent.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;
}
}
}
在MainActivity中的添加的subscriber方法如下:
@Subscribe(threadMode = ThreadMode.MAIN)
fun onReceiveEvent(event:MyEvent){
Log.i("MainActivity","onReceiveEvent,msg=${event.msg}")
}
在生成的AppEventBusIndex
类中可以看到已经将onReceiveEvent
方法保存起来了。
EventBus源码分析
EventBus的register方法会调用SubscriberMethodFinder
这个类去搜集SubscriberMethod
:
//org.greenrobot.eventbus.EventBus.java
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
SubscriberMethod
就是保存Method相关信息的类:
public class SubscriberMethod {
final Method method;
final ThreadMode threadMode;
final Class<?> eventType;
final int priority;
final boolean sticky;
//省略其它代码
}
SubscriberMethodFinder
的findSubscriberMethods
方法如下:
//org.greenrobot.eventbus.SubscriberMethodFinder.java
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
if (ignoreGeneratedIndex) {
//如果强制使用反射就走这里
//ignoreGeneratedIndex是在EventBusBuilder中设置的,默认是false
subscriberMethods = findUsingReflection(subscriberClass);
} else {
//默认走这里,优先查找subscrier index类
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;
}
}
ignoreGeneratedIndex表示是否强制使用反射,它是在EventBusBuilder中设置的,默认是false,也就是会优先去查找SubscriberInfoIndex的实现类。
再看它的findUsingInfo
方法:
//org.greenrobot.eventbus.SubscriberMethodFinder.java
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
//查找SubscriberMethod的关键代码:
findState.subscriberInfo = getSubscriberInfo(findState);
if (findState.subscriberInfo != null) {
//通过apt已经找到了SubscriberMethod
SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
for (SubscriberMethod subscriberMethod : array) {
if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
findState.subscriberMethods.add(subscriberMethod);
}
}
} else {
//使用反射去查找SubscriberMethod
findUsingReflectionInSingleClass(findState);
}
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}
可以看到,关键代码:
findState.subscriberInfo = getSubscriberInfo(findState);
当subscriberInfo不为空就表示已经有SubscriberInfoIndex实现类,直接从这里面获取SubscriberInfo,如果不成立就用反射去查找。
SubscriberInfo里保存了SubscriberMethod的集合:
public interface SubscriberInfo {
Class<?> getSubscriberClass();
SubscriberMethod[] getSubscriberMethods();
SubscriberInfo getSuperSubscriberInfo();
boolean shouldCheckSuperclass();
}
然后看getSubscriberInfo
方法实现:
//org.greenrobot.eventbus.SubscriberMethodFinder.java
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这个集合中查到subscriberInfoIndex
的实现类,而subscriberInfoIndex的实现类是apt自动生成的,并且通过EventBusBuilder的addIndex在app启动时添加到subscriberInfoIndexes中。这样也就避免了反射去查找subscriberInfo带来的性能问题。