源码解析|Android EventBus3源码解析(下)

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

    推荐阅读