在上一篇
EventBus3.0源码解析
中,在介绍查找订阅方法时提到了
APT
解析,当时一笔带过,主要是觉得这个特性比较重要,所以单独拎出来写一篇来介绍。
源码
先来回忆下查找订阅方法:
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
if (ignoreGeneratedIndex) {
// 使用反射查找
subscriberMethods = findUsingReflection(subscriberClass);
} else {
// 使用生成的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;
}
}
复制代码
在
findSubscriberMethods
方法中,会根据
ignoreGeneratedIndex
来进行策略执行,这个变量默认是
false
,调用了
findUsingInfo
方法:
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
// 准备一个findState实例
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
方法,它返回了一个
SubscriberInfo实例
并保存在了
FindState实例
中,来看看
SubscriberInfo
里有什么:
public interface SubscriberInfo {
// 订阅类
Class<?> getSubscriberClass();
// 订阅方法集合
SubscriberMethod[] getSubscriberMethods();
SubscriberInfo getSuperSubscriberInfo();
boolean shouldCheckSuperclass();
}
复制代码
SubscriberInfo
是一个接口,通过这个接口可以拿到封装了订阅类、订阅方法以及父类的
SubscriberInfo
。再回到上一步,看看关键方法
getSubscriberInfo
:
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;
}
}
// 这里判断了subscriberInfoIndexes不为空,从subscriberInfoIndexes中去取SubscriberInfo
if (subscriberInfoIndexes != null) {
for (SubscriberInfoIndex index : subscriberInfoIndexes) {
SubscriberInfo info = index.getSubscriberInfo(findState.clazz);
if (info != null) {
return info;
}
}
}
return null;
}
复制代码
一共两步,第一步是在查找过一次之后并且找到了目标
SubscriberInfo
之后才会执行。重点是在第二步,判断了一个参数
subscriberInfoIndexes
不为空,然后从
subscriberInfoIndexes
中遍历出目标类的
SubscriberInfo
。那么
subscriberInfoIndexes
又是什么呢:
class SubscriberMethodFinder {
// 一个SubscriberInfoIndex集合
private List<SubscriberInfoIndex> subscriberInfoIndexes;
...
}
// 一个根据订阅类查找订阅信息的接口
public interface SubscriberInfoIndex {
SubscriberInfo getSubscriberInfo(Class<?> subscriberClass);
}
复制代码
subscriberInfoIndexes
是一个
SubscriberInfoIndex
集合,它是在
EventBus初始化
时被赋值的:
public class EventBus {
EventBus(EventBusBuilder builder) {
...
// 传入builder.subscriberInfoIndexes
subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
builder.strictMethodVerification, builder.ignoreGeneratedIndex);
...
}
}
复制代码
而
EventBus初始化
则是使用的
Builder模式
,Builder里有个
addIndex
方法:
public EventBusBuilder addIndex(SubscriberInfoIndex index) {
if(subscriberInfoIndexes == null) {
subscriberInfoIndexes = new ArrayList<>();
}
subscriberInfoIndexes.add(index);
return this;
}
复制代码
到了这里显而易见,
SubscriberInfoIndex
是由我们传参进
Builder
的。其实
SubscriberInfoIndex
就是
APT
预先解析出来的
订阅者信息提供者
,它需要开发者自行在编译器中配置
APT
后编译生成,接下来就来看看如何才能生成并使用索引类
SubscriberInfoIndex
。
Subscriber Index
APT(Annotation Processing Tool)配置
首先要在项目module的
build.gradle
中引入
EventBusAnnotationProcessor依赖
, 并设置生成类参数
android {
defaultConfig {
javaCompileOptions {
// 注解处理器参数配置
annotationProcessorOptions {
// 配置参数名和值
arguments = [ eventBusIndex : 'com.example.myapp.MyEventBusIndex' ]
}
}
}
}
dependencies {
// 注解依赖
implementation 'org.greenrobot:eventbus:3.0.0'
// 注解处理器依赖
annotationProcessor 'org.greenrobot:eventbus-annotation-processor:3.0.0'
}
复制代码
如果使用的是
Kotlin
语言,就需要使用
Kotlin
的专用APT:
kapt
apply plugin: 'kotlin-kapt' // ensure kapt plugin is applied
dependencies {
implementation 'org.greenrobot:eventbus:3.0.0'
kapt 'org.greenrobot:eventbus-annotation-processor:3.0.0'
}
kapt {
arguments {
// 包名可以自定义
arg('eventBusIndex', 'com.example.myapp.MyEventBusIndex')
}
}
复制代码
如果项目中是
Java
和
Kotlin
同时使用,可能会存在
annotationProcessor
和
kapt
同时存在,建议统一改为
kapt
,因为后者会兼容前者。
Index应用
在上一步完成后,只需要
Rebuild Project
,就会在项目路径
app-build-generated-source-apt/kapt
下看到生成的索引类:
MyEventBusIndex.java
,它实现了
SubscriberInfoIndex
接口,实现了
getSubscriberInfo
方法。从上边的源码分析就知道,接下来只需要将生成的索引类传参进
EventBus
中:
EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus();
// Now the default instance uses the given index. Use it like this:
EventBus eventBus = EventBus.getDefault();
复制代码
由源码可知,
EventBus
中保存的是一个
Subscriber Index集合
,所以
addIndex
方法可以调用多次,这样,在
非application
的
Model
中也可以生成各自的Index类,最后统一添加到
EventBus
中。
来看看生成的索引类
MyEventBusIndex.java
:
/** This class is generated by EventBus, do not edit. */
public class MyEventBusIndex implements SubscriberInfoIndex {
private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX;
// 类初始化时直接赋值
static {
SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>();
// 直接new一个SimpleSubscriberInfo并加入SUBSCRIBER_INDEX
putIndex(new SimpleSubscriberInfo(com.example.myapp.MainActivity.Companion.class,
true, new SubscriberMethodInfo[] {
new SubscriberMethodInfo("handleEvent", com.example.myapp.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;
}
}
}
复制代码
MyEventBusIndex
在初始化时,直接会将订阅者相关信息缓存在Map中,并实现了
SubscriberInfoIndex接口
,在实现方法
getSubscriberInfo
中根据订阅类返回对应的订阅者信息。到了这里,你肯定会疑惑,
EventBus
是如何在编译期间就找到了所有订阅信息并且是如何生成了一个
Java
文件的,奥秘就在
APT
技术中。
EventBusAnnotationProcessor
在上文中,整个
MyEventBusIndex
文件都是由
APT
在编译时解析注解生成的。显然,
EventBus
自定义了自己的注解处理器。
我们知道,自定义注解处理器只要三步:
-
新建自定义注解器(其实就是一个java类),继承自
Java
提供的AbstractProcessor
-
实现
process
方法,开发者就是在这个方法中进行注解解析并生成Java类 -
注册到
Javac
(编译器)
这样,在编译期间,
Javac
会扫描注解,检查
AbstractProcessor
的子类,并且调用该子类的
process
函数,然后将添加了注解的所有元素都传递到
process
函数中,执行我们的处理逻辑,生成我们想要的Java文件。
那么重点就在
EventBus
的
EventBusAnnotationProcessor
的
process
方法。在开始之前,需要了解下
Element
类:
Element
要想自定义
APT
,必须掌握
Element
,它是
APT
技术的基础。
Element
只有在编译期间是可见的,因为它是用来在编译期间描述Java文件的静态结构的一种类型,
Element
可以表示包、类或方法。
JDK
提供了5种
Element
,它们都继承自
Element
:
- javax.lang.model.element.Element
- javax.lang.model.element.ExecutableElement
- javax.lang.model.element.PackageElement
- javax.lang.model.element.TypeElement
- javax.lang.model.element.TypeParameterElement
- javax.lang.model.element.VariableElement
复制代码
它们分别表示:
-
PackageElement
:表示一个包程序元素。提供对有关包及其成员的信息的访问。 -
TypeElement
:表示一个类或接口程序元素。提供对有关类型及其成员的信息的访问。注意,枚举类型是一种类,而注释类型是一种接口。 -
ExecutableElement
:表示某个类或接口的方法、构造方法或初始化程序,包括注释类型元素。 -
TypeParameterElement
:表示一般类、接口、方法或构造方法元素的形式类型(泛型)参数。 -
VariableElement
:表示一个字段、enum 常量、方法或构造方法参数、局部变量或异常参数。
举个栗子:
package com.example; // PackageElement
public class Demo<T extends List> { // Demo类是TypeElement,T是TypeParameterElement
private int a; // VariableElement
private String b; // VariableElement
public Demo () {} // ExecuteableElement
public void setValue (int value) {} // 方法setValue是ExecuteableElement,参数value是VariableElement
}
复制代码
再介绍下几个涉及到的
Element
方法:
asType
:返回此元素定义的类型。返回值用
TypeMirror
表示,
TypeMirror
表示了Java编程语言中的类型,包括基本类型,一般用来做类型判断。
getModifiers
:返回此元素的修饰符,不包括注释。但包括显式修饰符,比如接口成员的
public
和
static
修饰符。
getEnclosingElement
:返回封装此元素的最里层元素。
- 如果此元素的声明在词法上直接封装在另一个元素的声明中,则返回那个封装元素。
- 如果此元素是顶层类型,则返回它的包。
-
如果此元素是一个包,则返回
null
。 -
如果此元素是一个类型参数,则返回
null
。
process
接下来进入正题,看看
EventBus
注解处理器的
process
方法:
public class EventBusAnnotationProcessor extends AbstractProcessor {
public static final String OPTION_EVENT_BUS_INDEX = "eventBusIndex";
/** Found subscriber methods for a class (without superclasses). */
// 自定义的数据结构,保存`<Key, List<Value>>`类型数据;这里的key是订阅类
private final ListMap<TypeElement, ExecutableElement> methodsByClass = new ListMap<>();
// 保存不合法的元素,这里的key是订阅类
private final Set<TypeElement> classesToSkip = new HashSet<>();
...
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) {
// messager用于输出log
Messager messager = processingEnv.getMessager();
try