天师

天下事有难易乎?为之,则难者亦易矣;不为,则易者亦难矣。


  • 首页

  • 分类

  • 归档

  • 标签

  • 关于

EventBus巧用APT插件提升性能

发表于 2019-07-30 更新于 2019-07-31 分类于 源码分析
本文字数: 16k 阅读时长 ≈ 14 分钟

在上一篇研究了EventBus源码时,默认情况下EventBus是通过在运行时反射的方式去获取订阅者中所有@Subscribe注解的方法信息,这种方式是比较损耗性能的。

EventBus是如何提升效率?

与Butterknife类似,采用编译时注解,在项目编译时将所有的订阅者注解的方法集中到生成class文件中,通过这种方式有效的避免了反射获取注解类的方法信息带来的性能损耗。

基本使用

在项目中集成apt插件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
android {
defaultConfig {
javaCompileOptions {
annotationProcessorOptions {
//通过自定义注解处理器生成索引类,如果不配置,类则不生成,下面分析,verbose是否打印日志
arguments = [eventBusIndex : applicationId + '.eventbus.CustomEventBusIndex',
verbose : "true"]
}
}
}
}
dependencies {
//加入EventBus
implementation 'org.greenrobot:eventbus:3.1.1'
//apt加入
annotationProcessor 'org.greenrobot:eventbus-annotation-processor:3.1.1'
}

先Make Project,apt插件会帮我们生成CustomEventBusIndex

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class CustomEventBusIndex implements SubscriberInfoIndex {
private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX;

//对象初始化的时候将所有订阅者及注解方法信息统一存储
static {
SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>();

putIndex(new SimpleSubscriberInfo(com.custom.plugin.MainActivity.class, true, new SubscriberMethodInfo[] {
new SubscriberMethodInfo("doSomething", com.custom.plugin.bean.SendBean.class, ThreadMode.MAIN, 100, true),
new SubscriberMethodInfo("doBackgroundThing", com.custom.plugin.bean.TBean.class, ThreadMode.MAIN_ORDERED,
100, false),
}));
}

private static void putIndex(SubscriberInfo info) {
SUBSCRIBER_INDEX.put(info.getSubscriberClass(), info);
}

/**
* 该方法在SubscriberMethodFinder#findUsingInfo()中调用
*/
@Override
public SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) {
SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass);
if (info != null) {
return info;
} else {
return null;
}
}
}

EventBus自定义初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
//通过build方式构建EventBus
EventBus.builder()
//若为true,则是通过反射方式,默认是false,先从获取eventBusIndex,若为null再通过反射调用
.ignoreGeneratedIndex(false)
//自定义添加apt生成的类
.addIndex(new CustomEventBusIndex())
//构建EventBus
.installDefaultEventBus();
}
}

通过APT插件获取所有订阅者注解信息放入生成class类中,避免运行时反射带来的性能损耗了。

插件源码分析

  • 首先分析EventBusAnnotationProcessor#process ()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
/**
* 这个注解表示:告知Processor哪些注解需要处理(建议通过复写getSupportedAnnotationTypes()
* @SupportedAnnotationTypes()也是一种实现方式,适用于数量较少的情况
*/
@SupportedAnnotationTypes("org.greenrobot.eventbus.Subscribe")
@SupportedOptions(value = {"eventBusIndex", "verbose"})
public class EventBusAnnotationProcessor extends AbstractProcessor {
public static final String OPTION_EVENT_BUS_INDEX = "eventBusIndex";
public static final String OPTION_VERBOSE = "verbose";
//...省略:指定java版本、支持的注解类型
/**
* 这里扫描、处理注解、生成Java文件
*/
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) {
Messager messager = processingEnv.getMessager();
try {
//获取 eventBusIndex 配置项,如果没有获取到配置将会打印错误信息,并return false
String index = processingEnv.getOptions().get(OPTION_EVENT_BUS_INDEX);
if (index == null) {
messager.printMessage(Diagnostic.Kind.ERROR, "No option " + OPTION_EVENT_BUS_INDEX +" passed to annotation processor");
return false;
}
//获取 verbose 配置,是否打印日志
verbose = Boolean.parseBoolean(processingEnv.getOptions().get(OPTION_VERBOSE));
int lastPeriod = index.lastIndexOf('.');
//获取包名
String indexPackage = lastPeriod != -1 ? index.substring(0, lastPeriod) : null;

round++;
//打印编译时的一些信息
if (verbose) {
messager.printMessage(Diagnostic.Kind.NOTE, "Processing round " + round + ", new annotations: " + !annotations.isEmpty() + ", processingOver: " + env.processingOver());
}
if (env.processingOver()) {
if (!annotations.isEmpty()) {
messager.printMessage(Diagnostic.Kind.ERROR, "Unexpected processing state: annotations still available after processing over");
return false;
}
}
//没有扫描到相关注解直接return
if (annotations.isEmpty()) {
return false;
}
//状态异常情况下,直接return
if (writerRoundDone) {
messager.printMessage(Diagnostic.Kind.ERROR,
"Unexpected processing state: annotations still available after writing.");
}
//1.添加@Subscribe注解的订阅者及方法,放至集合中
collectSubscribers(annotations, env, messager);
//2.过滤掉订阅者 类与方法都是不可见(非public、static修饰)的情况
checkForSubscribersToSkip(messager, indexPackage);

if (!methodsByClass.isEmpty()) {
//3.生成java文件
createInfoIndexFile(index);
} else {
messager.printMessage(Diagnostic.Kind.WARNING, "No @Subscribe annotations found");
}
writerRoundDone = true;
} catch (RuntimeException e) {
// IntelliJ does not handle exceptions nicely, so log and print a message
e.printStackTrace();
messager.printMessage(Diagnostic.Kind.ERROR,
"Unexpected error in EventBusAnnotationProcessor: " + e);
}
return true;
}

主要通过分析添加订阅者,过滤非法订阅者或方法,生成java文件这三个步骤

  • 分析1 collectSubscribers()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private void collectSubscribers(Set<? extends TypeElement> annotations, RoundEnvironment env, Messager messager) {
for (TypeElement annotation : annotations) {
Set<? extends Element> elements = env.getElementsAnnotatedWith(annotation);
for (Element element : elements) {
//ExecutableElement类型:表示某个类/接口的方法、构造方法...
if (element instanceof ExecutableElement) {
ExecutableElement method = (ExecutableElement) element;
//方法的Modifier(修饰符)、Parameters(参数)检测
if (checkHasNoErrors(method, messager)) {
TypeElement classElement = (TypeElement) method.getEnclosingElement();
//将订阅者及注解方法添加至集合中,后续输出
methodsByClass.putElement(classElement, method);
}
} else {
//注解作用在其它属性上,打印错误信息
messager.printMessage(Diagnostic.Kind.ERROR, "@Subscribe is only valid for methods", element);
}
}
}
}

checkHasNoErrors() 过滤static、非public、无参的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private boolean checkHasNoErrors(ExecutableElement element, Messager messager) {
if (element.getModifiers().contains(Modifier.STATIC)) {
messager.printMessage(Diagnostic.Kind.ERROR, "Subscriber method must not be static", element);
return false;
}

if (!element.getModifiers().contains(Modifier.PUBLIC)) {
messager.printMessage(Diagnostic.Kind.ERROR, "Subscriber method must be public", element);
return false;
}

List<? extends VariableElement> parameters = ((ExecutableElement) element).getParameters();
if (parameters.size() != 1) {
messager.printMessage(Diagnostic.Kind.ERROR, "Subscriber method must have exactly 1 parameter", element);
return false;
}
return true;
}
  • 分析2 checkForSubscribersToSkip(),如果订阅者 类与方法都是不可见的 添加至过滤classesToSkip集合,后续进行过滤
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
private void checkForSubscribersToSkip(Messager messager, String myPackage) {
for (TypeElement skipCandidate : methodsByClass.keySet()) {
TypeElement subscriberClass = skipCandidate;
while (subscriberClass != null) {
//过滤订阅者,下面分析isVisible()
if (!isVisible(myPackage, subscriberClass)) {
boolean added = classesToSkip.add(skipCandidate);
if (added) {
String msg;
if (subscriberClass.equals(skipCandidate)) {
msg = "Falling back to reflection because class is not public";
} else {
msg = "Falling back to reflection because " + skipCandidate +
" has a non-public super class";
}
messager.printMessage(Diagnostic.Kind.NOTE, msg, subscriberClass);
}
break;
}
//这里是过滤订阅方法
List<ExecutableElement> methods = methodsByClass.get(subscriberClass);
if (methods != null) {
for (ExecutableElement method : methods) {
String skipReason = null;
VariableElement param = method.getParameters().get(0);
TypeMirror typeMirror = getParamTypeMirror(param, messager);
//判断方法中参数类型是不是class、interface、基本数据类型、java.util.Set<String>
//过滤掉非法参数
if (!(typeMirror instanceof DeclaredType) ||
!(((DeclaredType) typeMirror).asElement() instanceof TypeElement)) {
skipReason = "event type cannot be processed";
}
//过滤非public、static修饰方法
if (skipReason == null) {
TypeElement eventTypeElement = (TypeElement) ((DeclaredType) typeMirror).asElement();
if (!isVisible(myPackage, eventTypeElement)) {
skipReason = "event type is not public";
}
}
//添加至过滤集合
if (skipReason != null) {
boolean added = classesToSkip.add(skipCandidate);
if (added) {
String msg = "Falling back to reflection because " + skipReason;
if (!subscriberClass.equals(skipCandidate)) {
msg += " (found in super class for " + skipCandidate + ")";
}
messager.printMessage(Diagnostic.Kind.NOTE, msg, param);
}
break;
}
}
}
//遍历父类,继续过滤
subscriberClass = getSuperclass(subscriberClass);
}
}
}

上面有一个isVisible(),用于判断元素的修饰符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private boolean isVisible(String myPackage, TypeElement typeElement) {
//返回此元素的修饰符、不包括注解,但包括显式修饰符,比如接口成员的 public 和 static 修饰符
Set<Modifier> modifiers = typeElement.getModifiers();
boolean visible;
if (modifiers.contains(Modifier.PUBLIC)) {
visible = true;
} else if (modifiers.contains(Modifier.PRIVATE) || modifiers.contains(Modifier.PROTECTED)) {
visible = false;
} else {
//包名判断,写入文件时走这里
String subscriberPackage = getPackageElement(typeElement).getQualifiedName().toString();
if (myPackage == null) {
visible = subscriberPackage.length() == 0;
} else {
visible = myPackage.equals(subscriberPackage);
}
}
return visible;
}
  • 分析3 createInfoIndex(index),java文件生成
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
private void createInfoIndexFile(String index) {
BufferedWriter writer = null;
try {
//创建我们定义好的路径文件com.xx.xx.eventbus.CustomEventBusIndex.java
JavaFileObject sourceFile = processingEnv.getFiler().createSourceFile(index);
int period = index.lastIndexOf('.');
String myPackage = period > 0 ? index.substring(0, period) : null;
String clazz = index.substring(period + 1);
writer = new BufferedWriter(sourceFile.openWriter());
//下面就是代码的书写了,包名第一,然后就是导报,在是代码书写
if (myPackage != null) {
writer.write("package " + myPackage + ";\n\n");
}
writer.write("import org.greenrobot.eventbus.meta.SimpleSubscriberInfo;\n");
writer.write("import org.greenrobot.eventbus.meta.SubscriberMethodInfo;\n");
writer.write("import org.greenrobot.eventbus.meta.SubscriberInfo;\n");
writer.write("import org.greenrobot.eventbus.meta.SubscriberInfoIndex;\n\n");
writer.write("import org.greenrobot.eventbus.ThreadMode;\n\n");
writer.write("import java.util.HashMap;\n");
writer.write("import java.util.Map;\n\n");
writer.write("/** This class is generated by EventBus, do not edit. */\n");
writer.write("public class " + clazz + " implements SubscriberInfoIndex {\n");
writer.write(" private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX;\n\n");
writer.write(" static {\n");
writer.write(" SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>();\n\n");
//这里就是将上面获取的订阅者及@Subscribe注解方法的集合遍历写入,过滤上述不合适的订阅者及方法
writeIndexLines(writer, myPackage);
writer.write(" }\n\n");
writer.write(" private static void putIndex(SubscriberInfo info) {\n");
writer.write(" SUBSCRIBER_INDEX.put(info.getSubscriberClass(), info);\n");
writer.write(" }\n\n");
writer.write(" @Override\n");
writer.write(" public SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) {\n");
writer.write(" SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass);\n");
writer.write(" if (info != null) {\n");
writer.write(" return info;\n");
writer.write(" } else {\n");
writer.write(" return null;\n");
writer.write(" }\n");
writer.write(" }\n");
writer.write("}\n");
} catch (IOException e) {
throw new RuntimeException("Could not write source for " + index, e);
} finally {
if (writer != null) {
try {
//关流 刷新
writer.close();
} catch (IOException e) {
//Silent
}
}
}
}

让我们看看writeIndexLines(writer, myPackage)到底做了什么操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private void writeIndexLines(BufferedWriter writer, String myPackage) throws IOException {
for (TypeElement subscriberTypeElement : methodsByClass.keySet()) {
//过滤
if (classesToSkip.contains(subscriberTypeElement)) {
continue;
}
//getClassString()主要是参数包名的处理,如com.custom.plugin.MainActivity.class
String subscriberClass = getClassString(subscriberTypeElement, myPackage);
if (isVisible(myPackage, subscriberTypeElement)) {
writeLine(writer, 2,
"putIndex(new SimpleSubscriberInfo(" + subscriberClass + ".class,",
"true,", "new SubscriberMethodInfo[] {");
List<ExecutableElement> methods = methodsByClass.get(subscriberTypeElement);
writeCreateSubscriberMethods(writer, methods, "new SubscriberMethodInfo", myPackage);
writer.write(" }));\n\n");
} else {
writer.write(" // Subscriber not visible to index: " + subscriberClass + "\n");
}
}
}

很明显就是集合的遍历写入,其中包括过滤不合适的订阅者及方法。

到这里EventBusAnnotationProcessor类就分析完成了,接下来打算写一篇自定义ButterKnife文章,深刻学习自定义AbstractProcessor。

感谢观看。

# Android # 源码分析
EventBus源码分析
注解笔记
  • 文章目录
  • 站点概览
欢亮

欢亮

天下事有难易乎?为之,则难者亦易矣;不为,则易者亦难矣。
9 日志
6 分类
10 标签
GitHub E-Mail
Links
  • 二松同学
  1. 1. EventBus是如何提升效率?
  2. 2. 基本使用
  3. 3. 插件源码分析
© 2019 欢亮 | 121k | 1:50
由 Hexo 强力驱动 v3.9.0
|
主题 – NexT.Muse v7.3.0