前言#
本来想把EventBus的使用和编译库的分析一起说,但是觉得篇幅有点太大了,编译库的东西虽然不多也不复杂,但是还是有很多能学习到的东西。
在上一篇已经建议大家对apt 和注解的使用有了一定的了解之后再来,否则你可能看的一脸懵逼,如果你还接触他们,可以先看一下我之前写的博客:
正文#
使用apt编译工具,主要是实现process(Set<? extends TypeElement> annotations, RoundEnvironment env)方法,这里完成了生成类和使用注解的所有工作:
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) {
Messager messager = processingEnv.getMessager();
try {
// 通过命令行得到了EventBus的索引值,这个值通过apt工具类配置,用来找到程序的包名,也是文件要生成的位置
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 = 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;
}
}
if (annotations.isEmpty()) {
return false;
}
if (writerRoundDone) {
messager.printMessage(Diagnostic.Kind.ERROR,
"Unexpected processing state: annotations still available after writing.");
}
// 这里才是重点,开始收集Subscriber注解
collectSubscribers(annotations, env, messager);
// 开始检查某些注解是否要忽略
checkForSubscribersToSkip(messager, indexPackage);
// 检查是否被注解的方法的集合 是空的
if (!methodsByClass.isEmpty()) {
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;
}
这就是整个编译实现的过程,看着代码量很多,但是实际上我们关注的点只有几个,首先这个eventBusIndex参数是从哪来的呢?
String index = processingEnv.getOptions().get(OPTION_EVENT_BUS_INDEX);这个参数是在要运行的工程中配置的,你可以打开EventBusPerformance的build.gradle文件:
apt { arguments { eventBusIndex "org.greenrobot.eventbusperf.MyEventBusIndex" } }
我们重点要看的只有三个方法:
// 这里才是重点,开始收集Subscriber注解
collectSubscribers(annotations, env, messager);
// 开始检查某些注解是否要忽略
checkForSubscribersToSkip(messager, indexPackage);
// 检查是否被注解的方法的集合 是空的
if (!methodsByClass.isEmpty()) {
// 开始创建文件
createInfoIndexFile(index);
}
首先通过collectSubscribers(annotations, env, messager);收集所有的注解:
/**
* 收集所有的注解
* */
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) {
// 判断这个被注解的元素是否是一个方法
if (element instanceof ExecutableElement) {
ExecutableElement method = (ExecutableElement) element;
// 对这个方法进行必要的检查,不允许static/ 必须是public / 只能有一个参数
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);
}
}
}
}
上面的注释已经写的非常详细了,找到所有被注解的方法,然后会这些方法进行检查,都做了哪些限制呢?
/**
* 对方法进行检查
* */
private boolean checkHasNoErrors(ExecutableElement element, Messager messager) {
// 不允许是static静态方法
if (element.getModifiers().contains(Modifier.STATIC)) {
messager.printMessage(Diagnostic.Kind.ERROR, "Subscriber method must not be static", element);
return false;
}
// 只能是public
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;
}
看来EventBus对于方法的限制还是很严格的,不能是static,只能是public,而且还只能由一个参数。
注解的收集工作就到此结束,然后对这个集合再次筛选,检查某些注解是否要忽略,进入到checkForSubscribersToSkip(messager, indexPackage):
private void checkForSubscribersToSkip(Messager messager, String myPackage) {
// 遍历刚才获得类和其对应的注解的集合
for (TypeElement skipCandidate : methodsByClass.keySet()) {
// 得到类
TypeElement subscriberClass = skipCandidate;
// 开始循环判断判断,一直找到最顶端的父类
while (subscriberClass != null) {
// 检查类是否可见
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);
// 如果参数不是类或者是接口,不会处理
if (!(typeMirror instanceof DeclaredType) ||
!(((DeclaredType) typeMirror).asElement() instanceof TypeElement)) {
skipReason = "event type cannot be processed";
}
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);
}
}
}
这个方法里把刚才收集到的注解和对应的类,进行遍历,从子类到最顶端的父类,先检查类是否是可见的,再检查参数的合法性,只允许是类或者是接口,这也解释了为什么使用基本类型的方法无法接受到Event。
下面是判断类的可见的方法:
/**
* 判断一个类是否可见
* */
private boolean isVisible(String myPackage, TypeElement typeElement) {
// 获取类的修饰符
Set<Modifier> modifiers = typeElement.getModifiers();
boolean visible;
// 如果这个类是public的,返回true
if (modifiers.contains(Modifier.PUBLIC)) {
visible = true;
}
// 如果这个类是PRIVATE 或 PROTECTED 返回false
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;
}
从方法中总结,只有三种情况类是可见的:
1、类是public修饰。
2、类在最外层,不在任何包内。
3、正好在编译的文件的包下。
2、3是在比较极端的情况下,我们平时注意使用注解的类都是public的就OK了。
然后就是最后的一步,生成我们的源文件createInfoIndexFile(index):
/**
* 开始创建每个类的索引文件,也是生成的Java文件
*
* @param index 文件生成的位置
* */
private void createInfoIndexFile(String index) {
BufferedWriter writer = null;
try {
// 在指定的位置,创建一个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");
// 创建一个私有的不可变的静态变量SUBSCRIBER_INDEX,类型是Map<Class<?>, SubscriberInfo>
writer.write(" private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX;\n\n");
// static 方法块,用来初始化静态变量
writer.write(" static {\n");
writer.write(" SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>();\n\n");
// 这里把所有的类和方法都写到文件里了
writeIndexLines(writer, myPackage);
writer.write(" }\n\n");
// 定义putIndex方法,刚才的writeIndexLines中就是使用了这个方法
// 把我们的类名,还有注解的方法名,还有定义的优先级和线程信息都放入了集合中
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
}
}
}
}
在文件中引入了几个文件,都是在EventBus中定义的,大家都去看一看,这里就略过了。我们已经知道这个文件中,有一个静态变量SUBSCRIBER_INDEX,并且有对应的put和get方法,然后再去看writeIndexLines(writer, myPackage):
private void writeIndexLines(BufferedWriter writer, String myPackage) throws IOException {
// 开始遍历被注解的方法集合
for (TypeElement subscriberTypeElement : methodsByClass.keySet()) {
// 这里做了一个检查,查看是否这个方法被忽略了
if (classesToSkip.contains(subscriberTypeElement)) {
continue;
}
// 得到类名
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");
}
}
}
/**
* 写入被注解的方法
* */
private void writeCreateSubscriberMethods(BufferedWriter writer, List<ExecutableElement> methods,
String callPrefix, String myPackage) throws IOException {
// 开始遍历方法
for (ExecutableElement method : methods) {
// 得到放的参数
List<? extends VariableElement> parameters = method.getParameters();
// 得到第一个参数的类型
TypeMirror paramType = getParamTypeMirror(parameters.get(0), null);
// 得到这个类型的元素
TypeElement paramElement = (TypeElement) processingEnv.getTypeUtils().asElement(paramType);
// 得到方法名
String methodName = method.getSimpleName().toString();
// 得到类名,并且拼接了.class
String eventClass = getClassString(paramElement, myPackage) + ".class";
// 得到注解对象
Subscribe subscribe = method.getAnnotation(Subscribe.class);
List<String> parts = new ArrayList<>();
// 开始把字符放入到list中
// 这是方法名
parts.add(callPrefix + "(\"" + methodName + "\",");
String lineEnd = "),";
// 判断优先级
if (subscribe.priority() == 0 && !subscribe.sticky()) {
// 加入类名
if (subscribe.threadMode() == ThreadMode.POSTING) {
parts.add(eventClass + lineEnd);
} else {
// 加入类名
parts.add(eventClass + ",");
// 加入线程名
parts.add("ThreadMode." + subscribe.threadMode().name() + lineEnd);
}
} else {
// 加入类名
parts.add(eventClass + ",");
// 加入线程名
parts.add("ThreadMode." + subscribe.threadMode().name() + ",");
parts.add(subscribe.priority() + ",");
parts.add(subscribe.sticky() + lineEnd);
}
writeLine(writer, 3, parts.toArray(new String[parts.size()]));
if (verbose) {
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "Indexed @Subscribe at " +
method.getEnclosingElement().getSimpleName() + "." + methodName +
"(" + paramElement.getSimpleName() + ")");
}
}
}
这个方法代码很多但是逻辑很简单,就是把刚才经过层层筛选的注解个对应的类封装成SimpleSubscriberInfo对象,通过刚才的put方法,都保存到了SUBSCRIBER_INDEX中。
SimpleSubscriberInfo里面有类的信息,还有被注解的方法的信息,上面使用代码的形式定义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>();
putIndex(new SimpleSubscriberInfo(org.greenrobot.eventbusperf.testsubject.PerfTestEventBus.SubscriberClassEventBusAsync.class,
true, new SubscriberMethodInfo[] {
new SubscriberMethodInfo("onEventAsync", TestEvent.class, ThreadMode.ASYNC),
}));
putIndex(new SimpleSubscriberInfo(org.greenrobot.eventbusperf.testsubject.PerfTestEventBus.SubscribeClassEventBusBackground.class,
true, new SubscriberMethodInfo[] {
new SubscriberMethodInfo("onEventBackgroundThread", TestEvent.class, ThreadMode.BACKGROUND),
}));
putIndex(new SimpleSubscriberInfo(TestRunnerActivity.class, true, new SubscriberMethodInfo[] {
new SubscriberMethodInfo("onEventMainThread", TestFinishedEvent.class, ThreadMode.MAIN),
}));
putIndex(new SimpleSubscriberInfo(org.greenrobot.eventbusperf.testsubject.PerfTestEventBus.SubscribeClassEventBusMain.class,
true, new SubscriberMethodInfo[] {
new SubscriberMethodInfo("onEventMainThread", TestEvent.class, ThreadMode.MAIN),
}));
putIndex(new SimpleSubscriberInfo(org.greenrobot.eventbusperf.testsubject.SubscribeClassEventBusDefault.class,
true, new SubscriberMethodInfo[] {
new SubscriberMethodInfo("onEvent", TestEvent.class),
}));
}
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;
}
}
}
ok,这样我们就知道了刚才的Java源文件生成的内容,整个编译库就到此结束了。
总结#
编译库的作用主要是:把类和其中被注解的方法,封装成一个SubscriberInfo信息保存起来,SubscriberInfo里面不仅有类的信息,还有被注解的方法的必要的设置,例如sticky,threadMode等等,这些信息都会为框架层的实现逻辑服务。
ok,编译库我们就已经完美攻克了,接下来就是看看再Java框架层的实现逻辑了。
因为篇幅问题,我会漏掉一些方法的说明,所以我把我的工程连接发给大家,方便大家学习使用:https://github.com/li504799868/EventBus-master