Android EventBus3源码解析(上):讲解了订阅者注册注销原理。
Android EventBus3源码解析(中):讲解了事件发送接收原理。
本文主要讲解EventBus创建原理、反射的不足及解决方案。
EventBus创建 学会了EventBus的基本操作后,相信大家都注意到大部分时候我们都是在用默认的EventBus来完成任务的,例如下面这样。
EventBus.getDefault().postSticky(new StrMessage("incoming message"));
当然,我们也可以对EventBus进行定制。那么EventBus的创建是怎样实现的呢?EventBus的创建代码如下。
EventBus.builder()
.addIndex(...)
.executorService(...)
.logger(...)
.build();
很明显,这里用到了建造者模式。这就是建造者模式的优势所在,可以对一个对象进行深度定制。看看源码。
...
public static EventBusBuilder builder() {
return new EventBusBuilder();
}
...
/** Builds an EventBus based on the current configuration. */
public EventBus build() {
return new EventBus(this);
}
代码很简单,builder方法就是返回一个EventBus建造者对象。链式调用设定好参数后调用build方法把定制的EventBusn对象创建出来就好了。
如果只是想在默认EventBus上做修改,把build方法换成installDefaultEventBus方法就行了。
EventBus.builder()
.addIndex(...)
.executorService(...)
.logger(...)
.installDefaultEventBus();
看看installDefaultEventBus方法做了什么。
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;
}
}
其实也是差不多的,只是在创建前做了一个判断。如果默认的EventBus已经创建了,那就抛出异常。也就是说默认EventBus是不可以重复创建的,参数设定好就不能改了。
这里再回顾一下EventBus.getDefault()做了什么。
/** Convenience singleton for apps using a process-wide EventBus instance. */
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
调用这个方法时如果默认EventBus还没创建,就立即创建它,不修改任何参数。这就是为什么EventBus教程里强调初始化默认EventBus操作要放在Application的onCreate方法里了。
反射的不足及解决方案 在Android EventBus3源码解析(上)我们知道EventBus为了获取订阅者的注解消息而用到了反射。总所周知,反射会对程序性能造成影响。如果每次是通过反射来寻找订阅者的接收方法再调用,那程序的性能就大打折扣了。
那么EventBus又是怎么解决的呢?我们可以先回顾一下图片的三级加载机制。内存里没有这张图片,就去本地查找,本地还没有最后才从网络上下载下来。同理,EventBus也使用了这种缓存的策略,回顾一下订阅者注册时调用的方法findSubscriberMethods()
List> findSubscriberMethods(Class> subscriberClass) {
//1
List> subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}//2
if (ignoreGeneratedIndex) {
//3
subscriberMethods = findUsingReflection(subscriberClass);
} else {
//4
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;
}
}
看到代码块1就很明白了,EventBus是会把订阅者的接收方法缓存到内存的,要的时候就先看看缓存里有没有,有就拿出来,没有就反射调用,顺便缓存起来。这样一来,反射带来的影响也就只是在第一次调用接收方法的时候,之后就没事了。
Android EventBus3源码解析(上)并没有对代码块2进行详细解析,现在是时候了。ignoreGeneratedIndex这个参数在定制EventBus的时候是可以设定的,它是指是否无视索引。如果无视,就走代码3的反射法。不无视,就走代码4的索引法。
在分析索引法之前,我们先简单回顾使用索引的步骤。
//第一步:在gradle文件设置索引文件
//com.github.cyc.eventbus.subscriberindexdemo.MyEventbusIndex
//是指索引文件的package路径
javaCompileOptions {
annotationProcessorOptions {
arguments = [eventBusIndex: 'com.github.cyc.eventbus.subscriberindexdemo.MyEventbusIndex']
}
}//第二步,编写订阅者的事件处理方法,例如在MainActivity里编写如下代码
//StrMessage是自己定义的事件
@Subscribe(threadMode = ThreadMode.MAIN,priority = 3)
public void onHandleMessage(StrMessage msg){
button.setText(msg.getMsg());
}
rebuild一下项目,就会自动生成一个索引文件。
/** This class is generated by EventBus, do not edit. */
public class MyEventBusIndex implements SubscriberInfoIndex {
private static final Map, SubscriberInfo> SUBSCRIBER_INDEX;
static {
SUBSCRIBER_INDEX = new HashMap, SubscriberInfo>();
//1
putIndex(new SimpleSubscriberInfo(com.pyjtlk.eventbus.MainActivity.class, true, new SubscriberMethodInfo[] {
new SubscriberMethodInfo("onHandleMessage", com.pyjtlk.eventbus.StrMessage.class, ThreadMode.MAIN, 3, false),
}));
}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;
}
}
}
代码1处可以看到我们写的订阅者的事件方法的相关信息(注解信息、方法名等等)被保存封装起来并保存在一个HashMap里。这些代码都是EventBus利用反射获取注册者信息后自动编写的。
需要注意的是,这里的反射是在程序编译时执行的。而我们上文所讲的反射调用是在运行时进行的。
再回顾我们添加索引的步骤。
public class MyApp extends Application {
@Override
public void onCreate() {
super.onCreate();
EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus();
}
}
跳进addIndex方法。
/** Adds an index generated by EventBus' annotation preprocessor. */
public EventBusBuilder addIndex(SubscriberInfoIndex index) {
if (subscriberInfoIndexes == null) {
subscriberInfoIndexes = new ArrayList<>();
}
subscriberInfoIndexes.add(index);
return this;
}
很简单,就是放进一个列表里。接着回到我们刚刚提到的代码4,通过索引法来查询订阅者事件处理方法。
else {
//4
subscriberMethods = findUsingInfo(subscriberClass);
}...private List> findUsingInfo(Class> subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
//1
findState.subscriberInfo = getSubscriberInfo(findState);
if (findState.subscriberInfo != null) {
SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
//2
for (SubscriberMethod subscriberMethod : array) {
if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
findState.subscriberMethods.add(subscriberMethod);
}
}
} else {
//3
findUsingReflectionInSingleClass(findState);
}
//4
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}
代码1获取到索引集。代码块2循环遍历寻找注册者的事件处理方法。没有找到对应的索引情况下,就只能像之前那样使用反射(运行时)来查找了(代码3处)。看到代码4,同样,索引法也会顺便在注册者的父类找找事件响应方法。
关于代码自动生成 使用了EventBus的索引后,终于体会到自动生成代码的强大了。不仅是EventBus,其实ButterKnife、GreenDAO、ARouter等框架也有自动生成代码的功能,都是通过注解处理器来实现的,感兴趣的朋友查阅AbstractProcessor的相关资料来学习。
参考文章
EventBus索引:https://blog.csdn.net/augfun/article/details/82635116
【源码解析|Android EventBus3源码解析(下)】EventBus注解处理器:https://www.jianshu.com/p/6c6231b094e2
推荐阅读
- EventBus 3.0 从入门到精通——使用详解(二)
- 第三方库探究|What(EventBus的核心竟然只是这两个Map?)
- 事件|在java项目中使用EventBus的优缺点
- EventBus使用及优点
- Android应用层|Android事件总线(一)EventBus3.0用法全解析
- EventBus 3.0 从入门到精通——EventBus的应用场景
- Java|EventBus使用
- EventBus|EventBus3.0——索引的使用
- EventBus的基本使用