文章列表
EventBus源码解析(一)
EventBus源码解析(二)
EventBus源码解析(三)
前言
在EventBus源码解析(一)中,我们着重介绍了基本使用方法,解析了EventBus的注册流程的源码。
在EventBus源码解析(二)中,我们着重介绍了EventBus的发送事件的流程及源码是如何实现的。
今天,我们将介绍在EventBus3.0中引入的编译时注解及Subscriber Index
,用法上略有不同。
Subscriber Index
这是EventBus3.0中一个可选的配置,虽然没有要求使用索引,但是相对比于不使用索引的情况下索引能可以提升Android的性能。
使用索引的先决条件是,有@Subscriber注解的方法才能为Subscriber和Event均为public的类生成索引,但是由于Java注解自身的处理,匿名类不能识别@Subscribe annotations。
当然如果不使用索引的话, EventBus会使用运行时反射机制来完成订阅事件查找,只是性能上会差一些。
如何使用
先说注解处理器的配置,在高于2.2.0版本的使用AnnotationProcessor
,而低于2.2.0版本的是使用android-apt
。由于我们使用的android gradle插件的版本是3.3.2,所以不介绍低于2.2.0版本一下的配置。
看一下如何配置
android {
defaultConfig {
javaCompileOptions {
annotationProcessorOptions {
arguments = [ eventBusIndex : 'com.source.wxl.sourcecodestudyapp.MyEventBusIndex' ]
}
}
}
}
dependencies {
implementation 'org.greenrobot:eventbus:3.1.1'
annotationProcessor 'org.greenrobot:eventbus-annotation-processor:3.1.1'
}
使用annotationProcessor
属性添加注解处理器,并添加注解处理器选项参数eventBusIndex
用来指定生成索引类的全路径。所以我们在APP模块下的build.gradle文件中添加以上内容。然后rebuild工程。然后可以在/app/build/generated/source/apt/debug/你的包名
目录下看到我们参数中指定的的类MyEventBusIndex
。
贴一下这个类的源码
/** This class is generated by EventBus, do not edit. */
public class MyEventBusIndex implements SubscriberInfoIndex {
//注释1、表明此索引把订阅者类对象和订阅者信息的映射关系存储到一个map对象中,
private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX;
static {
SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>();
//注释2、静态代码块中,将我们的订阅者的信息(包括订阅者类、是否在父类中查找、订阅方法的信息集合)封装到`SimpleSubscriberInfo`对象中,然后调用`putIndex`方法
putIndex(new SimpleSubscriberInfo(com.source.wxl.sourcecodestudyapp.eventbus.ActivityA.class, true,
new SubscriberMethodInfo[] {
new SubscriberMethodInfo("onEvent", com.source.wxl.sourcecodestudyapp.eventbus.bean.MessageEvent.class),
new SubscriberMethodInfo("onEvent2", com.source.wxl.sourcecodestudyapp.eventbus.bean.MessageEvent.class),
new SubscriberMethodInfo("onReceiveMsg",
com.source.wxl.sourcecodestudyapp.eventbus.bean.MessageEvent.class, ThreadMode.MAIN),
}));
putIndex(new SimpleSubscriberInfo(com.source.wxl.sourcecodestudyapp.eventbus.BaseActivityA.class, true,
new SubscriberMethodInfo[] {
new SubscriberMethodInfo("onEventParent",
com.source.wxl.sourcecodestudyapp.eventbus.bean.MessageEvent.class),
}));
}
private static void putIndex(SubscriberInfo info) {
//注释3、就是把注释2中传进来的`SimpleSubscriberInfo`对象put到变量`SUBSCRIBER_INDEX`中。
//剩下那个方法就是获取`SimpleSubscriberInfo`对象了。
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;
}
}
}
先简单看一下注解处理器自动生成的那个类MyEventBusIndex
类的注释,看下都做了哪些工作。
接着看怎么使用Subscriber Index
;
在需要监听事件的类中,注册监听器,跟第一篇文章中讲到的注册方法有所不同。看一下怎么注册,用一个与前两篇文章不同的新例子
public class ActivityA extends BaseActivityA {
private static final String TAG = "ActivityA=========a";
TextView textView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_a);
textView = findViewById(R.id.text);
//注册
//获取EventBusBuilder对象
EventBusBuilder builder = EventBus.builder();
//为builder对象的属性subscriberInfoIndexes(类型是List<SubscriberInfoIndex>)初始化并将MyEventBusIndex对象添加进list中,
//并生成一个EventBus对象赋值给EventBus的defaultInstance变量中,当外部调用EventBus.getDefault();时就会把此对象返出来
builder.addIndex(new MyEventBusIndex()).installDefaultEventBus();
EventBus eventBus = EventBus.getDefault();
eventBus.register(this);
}
//省略订阅方法
}
ActivityA集成了BaseActivityA,把父类的代码贴一下
public class BaseActivityA extends AppCompatActivity {
private static final String TAG = "BaseActivityA=========a";
//省略无关方法...
@Subscribe
public void onEventParent(MessageEvent messageEvent){
Log.d(TAG, "onEventParent: ");
}
}
父类中也有一个订阅方法。
然后剩下的就跟第一种用法没有区别了。
源码分析
新的注册流程中,先获取EventBusBuilder
对象,然后把编译时自动生成MyEventBusIndex
对象添加进builder中然后installDefaultEventBus
,最后getDefault
获取默认的EventBus
对象。
在第一篇文章中我们提到EventBus对象中的属性是通过EventBusBuilder
对象传进来,现在我们修改了Builder中的属性subscriberInfoIndexes
的内容,并生成了默认的EventBus
对象。
看一下EventBus的构造方法中的内容
EventBus(EventBusBuilder builder) {
//省略部分代码
indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
builder.strictMethodVerification, builder.ignoreGeneratedIndex);
//省略部分代码
}
可以看到builder
中的subscriberInfoIndexes
属性影响了EventBus中的indexCount
和subscriberMethodFinder
两个变量,重点看一下第二个,因为我们是需要用SubscriberMethodFinder
对象的帮助去遍历订阅者并查找订阅方法的,subscriberInfoIndexes
最终会被传进SubscriberMethodFinder
中,
SubscriberMethodFinder(List<SubscriberInfoIndex> subscriberInfoIndexes, boolean strictMethodVerification,
boolean ignoreGeneratedIndex) {
this.subscriberInfoIndexes = subscriberInfoIndexes;
this.strictMethodVerification = strictMethodVerification;
this.ignoreGeneratedIndex = ignoreGeneratedIndex;
}
这样SubscriberMethodFinder
的subscriberInfoIndexes
不为空,而且list的size=1(这唯一的数据就是在初始化EventBus的时候,调用builder的addIndex方法传进来的MyEventBusIndex
的实例),还记不记得subscriberInfoIndexes
变量在哪里用到过,在第一篇文章中出现过的方法,再贴一下相关方法
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
//注释4
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);
}
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;
}
(注:这里对第一篇文章中的一个标记作一下解释,当时解析findSubscriberMethods
方法的时候,代码的第七行加了一个注释标记 ,ignoreGeneratedIndex
这个变量的判断,是对是否使用索引的判断,这个变量也是从builder
中传进来的,默认是false
,如果使用了索引并在builder
中修改此变量为true
,那就会强制使用反射的方式去查找订阅者了)
以上括号中的注只是对第一篇文章中存疑点的解释。
回到此方法中,在findUsingInfo
中的注释4处会调用getSubscriberInfo
方法,如果首次进来,findState.subscriberInfo
为null,所以会跳过相关代码,进入下一个if判断,与第一篇文章中讲解的逻辑不同的是,subscriberInfoIndexes
不为null,所以这次会走进这个分支,循环判断subscriberInfoIndexes
变量,调用getSubscriberInfo(findState.clazz)
方法,这样就会从myEventBusIndex
对象的SUBSCRIBER_INDEX
变量中拿出当前订阅者ActivityA
的信息对象,也就是MyEventBusIndex
类中静态代码块中的第一次调用putIndex
时的对象SimpleSubscriberInfo
,这个对象中,看一下这个类的代码
public class SimpleSubscriberInfo extends AbstractSubscriberInfo {
private final SubscriberMethodInfo[] methodInfos;
public SimpleSubscriberInfo(Class subscriberClass, boolean shouldCheckSuperclass, SubscriberMethodInfo[] methodInfos) {
//注意这里传的第二个参数为null,这个参数对应的是superSubscriberInfoClass-----注释5
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;
}
}
这样拿到SimpleSubscriberInfo
这个对象后,返回到注释4处继续往下走,再接调用SimpleSubscriberInfo
的getSubscriberMethods
方法,取出当前订阅者中订阅方法数组信息,接下来的逻辑就跟第一篇文章中讲到的逻辑差不多了,遍历订阅方法,二级检查并加入到findState
的subscriberMethods
变量中,然后moveToSuperclass
进入父类BaseActivityA
。
然后继续调用getSubscriberInfo
方法,此时findState.subscriberInfo
已经不为null了。看下第二个条件findState.subscriberInfo.getSuperSubscriberInfo()
,因为findState.subscriberInfo
其实就是SimpleSubscriberInfo
对象,在这个对象的构造方法中调用父类的构造方法,传的第二个参数为null(看注释5),也就导致findState.subscriberInfo.getSuperSubscriberInfo()
这个条件为null,所以getSubscriberInfo
方法中的第一个条件判断中的逻辑还是不会执行,继续执行第二个条件判断,取出来的对象是MyEventBusIndex
类静态代码块中的第二个putIndex方法放进去的SimpleSubscriberInfo
对象,这样父类中的订阅者信息也拿到了,剩下的逻辑就跟第一次一样了。把取到的父类中的订阅者中的订阅方法放到findState
的subscriberMethods
变量中。
这样所有的订阅方法就都取出来了,剩下的post事件的逻辑就是[第二篇文章]中的内容了
总结
第一篇文章核心思想是通过反射获取订阅者信息及订阅方法,而本篇文章讲的是在编译期就的到订阅者信息及订阅方法。在性能上此种方式有很大的提升,用官方的话就是在某些机型上性能提升了3倍之多。所以还是推荐使用此种方式。