前言#
本來(lái)想把EventBus的使用和編譯庫(kù)的分析一起說(shuō)萨驶,但是覺(jué)得篇幅有點(diǎn)太大了,編譯庫(kù)的東西雖然不多也不復(fù)雜艇肴,但是還是有很多能學(xué)習(xí)到的東西腔呜。
在上一篇已經(jīng)建議大家對(duì)apt 和注解的使用有了一定的了解之后再來(lái),否則你可能看的一臉懵逼再悼,如果你還接觸他們核畴,可以先看一下我之前寫(xiě)的博客:
正文#
使用apt編譯工具,主要是實(shí)現(xiàn)process(Set<? extends TypeElement> annotations, RoundEnvironment env)方法冲九,這里完成了生成類和使用注解的所有工作:
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) {
Messager messager = processingEnv.getMessager();
try {
// 通過(guò)命令行得到了EventBus的索引值谤草,這個(gè)值通過(guò)apt工具類配置,用來(lái)找到程序的包名,也是文件要生成的位置
String index = processingEnv.getOptions().get(OPTION_EVENT_BUS_INDEX);
// 檢查是否配置了參數(shù)
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));
// 此處通過(guò)字符串的截取得到了包名
int lastPeriod = index.lastIndexOf('.');
String indexPackage = lastPeriod != -1 ? index.substring(0, lastPeriod) : null;
round++;
// 接下來(lái)進(jìn)行了一些檢查丑孩,這個(gè)就不用看了
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.");
}
// 這里才是重點(diǎn)冀宴,開(kāi)始收集Subscriber注解
collectSubscribers(annotations, env, messager);
// 開(kāi)始檢查某些注解是否要忽略
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;
}
這就是整個(gè)編譯實(shí)現(xiàn)的過(guò)程,看著代碼量很多温学,但是實(shí)際上我們關(guān)注的點(diǎn)只有幾個(gè)略贮,首先這個(gè)eventBusIndex參數(shù)是從哪來(lái)的呢?
String index = processingEnv.getOptions().get(OPTION_EVENT_BUS_INDEX);這個(gè)參數(shù)是在要運(yùn)行的工程中配置的枫浙,你可以打開(kāi)EventBusPerformance的build.gradle文件:
apt { arguments { eventBusIndex "org.greenrobot.eventbusperf.MyEventBusIndex" } }
我們重點(diǎn)要看的只有三個(gè)方法:
// 這里才是重點(diǎn),開(kāi)始收集Subscriber注解
collectSubscribers(annotations, env, messager);
// 開(kāi)始檢查某些注解是否要忽略
checkForSubscribersToSkip(messager, indexPackage);
// 檢查是否被注解的方法的集合 是空的
if (!methodsByClass.isEmpty()) {
// 開(kāi)始創(chuàng)建文件
createInfoIndexFile(index);
}
首先通過(guò)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) {
// 判斷這個(gè)被注解的元素是否是一個(gè)方法
if (element instanceof ExecutableElement) {
ExecutableElement method = (ExecutableElement) element;
// 對(duì)這個(gè)方法進(jìn)行必要的檢查古拴,不允許static/ 必須是public / 只能有一個(gè)參數(shù)
if (checkHasNoErrors(method, messager)) {
// 找到這個(gè)方法的類
TypeElement classElement = (TypeElement) method.getEnclosingElement();
// 把類個(gè)方法保存起來(lái)
methodsByClass.putElement(classElement, method);
}
} else {
messager.printMessage(Diagnostic.Kind.ERROR, "@Subscribe is only valid for methods", element);
}
}
}
}
上面的注釋已經(jīng)寫(xiě)的非常詳細(xì)了箩帚,找到所有被注解的方法,然后會(huì)這些方法進(jìn)行檢查黄痪,都做了哪些限制呢紧帕?
/**
* 對(duì)方法進(jìn)行檢查
* */
private boolean checkHasNoErrors(ExecutableElement element, Messager messager) {
// 不允許是static靜態(tài)方法
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;
}
// 只能含有一個(gè)方法參數(shù)
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;
}
看來(lái)EventBus對(duì)于方法的限制還是很嚴(yán)格的,不能是static桅打,只能是public是嗜,而且還只能由一個(gè)參數(shù)。
注解的收集工作就到此結(jié)束挺尾,然后對(duì)這個(gè)集合再次篩選鹅搪,檢查某些注解是否要忽略,進(jìn)入到checkForSubscribersToSkip(messager, indexPackage):
private void checkForSubscribersToSkip(Messager messager, String myPackage) {
// 遍歷剛才獲得類和其對(duì)應(yīng)的注解的集合
for (TypeElement skipCandidate : methodsByClass.keySet()) {
// 得到類
TypeElement subscriberClass = skipCandidate;
// 開(kāi)始循環(huán)判斷判斷遭铺,一直找到最頂端的父類
while (subscriberClass != null) {
// 檢查類是否可見(jiàn)
if (!isVisible(myPackage, subscriberClass)) {
// 如果類是不可見(jiàn)的丽柿,把他保存到不可見(jiàn)類的集合中
boolean added = classesToSkip.add(skipCandidate);
// 打印出錯(cuò)誤日志
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;
}
// 獲取這個(gè)類中被注解的方法
List<ExecutableElement> methods = methodsByClass.get(subscriberClass);
// 判空
if (methods != null) {
// 遍歷方法
for (ExecutableElement method : methods) {
String skipReason = null;
// 得到第一個(gè)參數(shù)
VariableElement param = method.getParameters().get(0);
// 得到參數(shù)的類型
TypeMirror typeMirror = getParamTypeMirror(param, messager);
// 如果參數(shù)不是類或者是接口,不會(huì)處理
if (!(typeMirror instanceof DeclaredType) ||
!(((DeclaredType) typeMirror).asElement() instanceof TypeElement)) {
skipReason = "event type cannot be processed";
}
if (skipReason == null) {
// 獲取這個(gè)元素的類名
TypeElement eventTypeElement = (TypeElement) ((DeclaredType) typeMirror).asElement();
// 判斷類名是否可見(jiàn)魂挂,否則也不處理
if (!isVisible(myPackage, eventTypeElement)) {
skipReason = "event type is not public";
}
}
// 如果經(jīng)過(guò)上面的檢查甫题,這個(gè)注解要被忽略
if (skipReason != null) {
// 添加到被忽略的結(jié)合中,并且出書(shū)錯(cuò)誤日志
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;
}
}
}
// 找到自己的父類涂召,再次循環(huán)
subscriberClass = getSuperclass(subscriberClass);
}
}
}
這個(gè)方法里把剛才收集到的注解和對(duì)應(yīng)的類坠非,進(jìn)行遍歷,從子類到最頂端的父類果正,先檢查類是否是可見(jiàn)的炎码,再檢查參數(shù)的合法性,只允許是類或者是接口秋泳,這也解釋了為什么使用基本類型的方法無(wú)法接受到Event辅肾。
下面是判斷類的可見(jiàn)的方法:
/**
* 判斷一個(gè)類是否可見(jiàn)
* */
private boolean isVisible(String myPackage, TypeElement typeElement) {
// 獲取類的修飾符
Set<Modifier> modifiers = typeElement.getModifiers();
boolean visible;
// 如果這個(gè)類是public的,返回true
if (modifiers.contains(Modifier.PUBLIC)) {
visible = true;
}
// 如果這個(gè)類是PRIVATE 或 PROTECTED 返回false
else if (modifiers.contains(Modifier.PRIVATE) || modifiers.contains(Modifier.PROTECTED)) {
visible = false;
}
// 其他情況
else {
// 獲取完整的包名
String subscriberPackage = getPackageElement(typeElement).getQualifiedName().toString();
// 如果包名是空的轮锥,說(shuō)明類是在最外層矫钓,是可見(jiàn)的
if (myPackage == null) {
visible = subscriberPackage.length() == 0;
} else {
// 判斷包名是否和類的包名相同,同樣是可見(jiàn)的
visible = myPackage.equals(subscriberPackage);
}
}
return visible;
}
從方法中總結(jié),只有三種情況類是可見(jiàn)的:
1新娜、類是public修飾赵辕。
2、類在最外層概龄,不在任何包內(nèi)还惠。
3、正好在編譯的文件的包下私杜。
2蚕键、3是在比較極端的情況下,我們平時(shí)注意使用注解的類都是public的就OK了衰粹。
然后就是最后的一步锣光,生成我們的源文件createInfoIndexFile(index):
/**
* 開(kāi)始創(chuàng)建每個(gè)類的索引文件,也是生成的Java文件
*
* @param index 文件生成的位置
* */
private void createInfoIndexFile(String index) {
BufferedWriter writer = null;
try {
// 在指定的位置铝耻,創(chuàng)建一個(gè)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);
// 創(chuàng)建字符輸出流
writer = new BufferedWriter(sourceFile.openWriter());
// 寫(xiě)入包名
if (myPackage != null) {
writer.write("package " + myPackage + ";\n\n");
}
// 寫(xiě)入要引入的包和類
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");
// 這里開(kāi)始定義類
writer.write("public class " + clazz + " implements SubscriberInfoIndex {\n");
// 創(chuàng)建一個(gè)私有的不可變的靜態(tài)變量SUBSCRIBER_INDEX誊爹,類型是Map<Class<?>, SubscriberInfo>
writer.write(" private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX;\n\n");
// static 方法塊,用來(lái)初始化靜態(tài)變量
writer.write(" static {\n");
writer.write(" SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>();\n\n");
// 這里把所有的類和方法都寫(xiě)到文件里了
writeIndexLines(writer, myPackage);
writer.write(" }\n\n");
// 定義putIndex方法瓢捉,剛才的writeIndexLines中就是使用了這個(gè)方法
// 把我們的類名频丘,還有注解的方法名,還有定義的優(yōu)先級(jí)和線程信息都放入了集合中
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
}
}
}
}
在文件中引入了幾個(gè)文件泡态,都是在EventBus中定義的搂漠,大家都去看一看,這里就略過(guò)了某弦。我們已經(jīng)知道這個(gè)文件中状答,有一個(gè)靜態(tài)變量SUBSCRIBER_INDEX,并且有對(duì)應(yīng)的put和get方法刀崖,然后再去看writeIndexLines(writer, myPackage):
private void writeIndexLines(BufferedWriter writer, String myPackage) throws IOException {
// 開(kāi)始遍歷被注解的方法集合
for (TypeElement subscriberTypeElement : methodsByClass.keySet()) {
// 這里做了一個(gè)檢查惊科,查看是否這個(gè)方法被忽略了
if (classesToSkip.contains(subscriberTypeElement)) {
continue;
}
// 得到類名
String subscriberClass = getClassString(subscriberTypeElement, myPackage);
// 檢查這個(gè)類是否可見(jiàn)
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");
}
}
}
/**
* 寫(xiě)入被注解的方法
* */
private void writeCreateSubscriberMethods(BufferedWriter writer, List<ExecutableElement> methods,
String callPrefix, String myPackage) throws IOException {
// 開(kāi)始遍歷方法
for (ExecutableElement method : methods) {
// 得到放的參數(shù)
List<? extends VariableElement> parameters = method.getParameters();
// 得到第一個(gè)參數(shù)的類型
TypeMirror paramType = getParamTypeMirror(parameters.get(0), null);
// 得到這個(gè)類型的元素
TypeElement paramElement = (TypeElement) processingEnv.getTypeUtils().asElement(paramType);
// 得到方法名
String methodName = method.getSimpleName().toString();
// 得到類名,并且拼接了.class
String eventClass = getClassString(paramElement, myPackage) + ".class";
// 得到注解對(duì)象
Subscribe subscribe = method.getAnnotation(Subscribe.class);
List<String> parts = new ArrayList<>();
// 開(kāi)始把字符放入到list中
// 這是方法名
parts.add(callPrefix + "(\"" + methodName + "\",");
String lineEnd = "),";
// 判斷優(yōu)先級(jí)
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() + ")");
}
}
}
這個(gè)方法代碼很多但是邏輯很簡(jiǎn)單亮钦,就是把剛才經(jīng)過(guò)層層篩選的注解個(gè)對(duì)應(yīng)的類封裝成SimpleSubscriberInfo對(duì)象馆截,通過(guò)剛才的put方法,都保存到了SUBSCRIBER_INDEX中蜂莉。
SimpleSubscriberInfo里面有類的信息蜡娶,還有被注解的方法的信息,上面使用代碼的形式定義Java源文件映穗,所以很亂窖张,我們知道了他的實(shí)現(xiàn)過(guò)程就可以了,我們可以在運(yùn)行的程序編譯后蚁滋,查看生成的文件的內(nèi)容:
/** 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源文件生成的內(nèi)容赘淮,整個(gè)編譯庫(kù)就到此結(jié)束了。
總結(jié)#
編譯庫(kù)的作用主要是:把類和其中被注解的方法睦霎,封裝成一個(gè)SubscriberInfo信息保存起來(lái)梢卸,SubscriberInfo里面不僅有類的信息,還有被注解的方法的必要的設(shè)置副女,例如sticky蛤高,threadMode等等,這些信息都會(huì)為框架層的實(shí)現(xiàn)邏輯服務(wù)碑幅。
ok戴陡,編譯庫(kù)我們就已經(jīng)完美攻克了,接下來(lái)就是看看再Java框架層的實(shí)現(xiàn)邏輯了沟涨。
因?yàn)槠鶈?wèn)題恤批,我會(huì)漏掉一些方法的說(shuō)明,所以我把我的工程連接發(fā)給大家拷窜,方便大家學(xué)習(xí)使用:https://github.com/li504799868/EventBus-master