EventBus3.0解析之注解处理器

1,643 阅读13分钟

在上一篇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')
    }
}

如果项目中是JavaKotlin同时使用,可能会存在annotationProcessorkapt同时存在,建议统一改为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方法可以调用多次,这样,在非applicationModel中也可以生成各自的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自定义了自己的注解处理器。

我们知道,自定义注解处理器只要三步:

  1. 新建自定义注解器(其实就是一个java类),继承自Java提供的AbstractProcessor
  2. 实现process方法,开发者就是在这个方法中进行注解解析并生成Java类
  3. 注册到Javac(编译器)

这样,在编译期间,Javac会扫描注解,检查AbstractProcessor的子类,并且调用该子类的process函数,然后将添加了注解的所有元素都传递到process函数中,执行我们的处理逻辑,生成我们想要的Java文件。

那么重点就在EventBusEventBusAnnotationProcessorprocess方法。在开始之前,需要了解下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:返回此元素的修饰符,不包括注释。但包括显式修饰符,比如接口成员的 publicstatic 修饰符。

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 {
            // 获取到我们在gradle中为apt设置的参数值,即生成类的完整路径,通过它可以获得包名和类名
            String index = processingEnv.getOptions().get(OPTION_EVENT_BUS_INDEX);
            if (index == null) {
                return false;
            }
            ...
            
            // 取出所有订阅者信息
            collectSubscribers(annotations, env, messager);
            // 检查订阅者信息
            checkForSubscribersToSkip(messager, indexPackage);

            if (!methodsByClass.isEmpty()) {
                // 查找到的注解的方法不为空,生成Java文件
                createInfoIndexFile(index);
            } else {
                messager.printMessage(Diagnostic.Kind.WARNING, "No @Subscribe annotations found");
            }
            writerRoundDone = true;
        } catch (RuntimeException e) {
            e.printStackTrace();
            messager.printMessage(Diagnostic.Kind.ERROR, e);
        }
        return true;
    }
    
}

process方法中做了三件事:

  1. 查找到所有订阅者
  2. 检查出不合法的订阅者
  3. 根据前两步的结果生成Java文件

一个一个方法来看。

collectSubscribers

    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) {
                // 元素必须是方法,因为@Subscribe只能注解方法
                if (element instanceof ExecutableElement) {
                    ExecutableElement method = (ExecutableElement) element;
                    // 检查方法,条件:订阅方法必须是非静态的,公开的,参数只能有一个
                    if (checkHasNoErrors(method, messager)) {
                        // 取封装订阅方法的类
                        TypeElement classElement = (TypeElement) method.getEnclosingElement();
                        // 以类名为key,保存订阅方法
                        methodsByClass.putElement(classElement, method);
                    }
                } else {
                    messager.printMessage(Diagnostic.Kind.ERROR, "@Subscribe is only valid for methods", element);
                }
            }
        }
    }

这里的流程也是很简单的,就是遍历被注解标记的所有元素,筛选出合法的订阅方法,具体看注释即可。checkHasNoErrors方法比较简单,就不贴代码了。 这里还有个陌生的方法:

RoundEnvironmentgetElementsAnnotatedWith方法, 这个方法会返回被当前注解标记的所有元素,可能是类、变量、方法等。

在方法最后,将筛选出来的订阅者和方法都保存在了methodsByClass这个容器里,这个容器的数据结构是<KEY, <List<Value>>>,可以存储订阅类中的多个方法。

checkForSubscribersToSkip

查找不合法的订阅者

private void checkForSubscribersToSkip(Messager messager, String myPackage) {
        // 遍历所有订阅类
        for (TypeElement skipCandidate : methodsByClass.keySet()) {
            TypeElement subscriberClass = skipCandidate;
            //开启子类到父类的while循环检查
            while (subscriberClass != null) {
                // 订阅类必须是可访问的,否则记录并直接break,检查下一个订阅类
                if (!isVisible(myPackage, subscriberClass)) {
                    boolean added = classesToSkip.add(skipCandidate);
                    ...
                    break;
                }
                // 拿到订阅类的所有订阅方法
                List<ExecutableElement> methods = methodsByClass.get(subscriberClass);
                if (methods != null) {
                    // 检查所有订阅方法
                    for (ExecutableElement method : methods) {
                        String skipReason = null;
                        // 拿到订阅方法的参数,开始对参数进行检查
                        VariableElement param = method.getParameters().get(0);
                        // 取得参数(Event)的类型
                        TypeMirror typeMirror = getParamTypeMirror(param, messager);
                        // 参数(Event)的类型必须是类或接口
                        if (!(typeMirror instanceof DeclaredType) ||
                                !(((DeclaredType) typeMirror).asElement() instanceof TypeElement)) {
                            skipReason = "event type cannot be processed";
                        }
                        if (skipReason == null) {
                            // 拿到参数(Event)元素
                            TypeElement eventTypeElement = (TypeElement) ((DeclaredType) typeMirror).asElement();
                            // 检查参数(Event)元素是不是可访问的
                            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;
                        }
                    }
                }
                // 切换到父类,若父类是系统的类,则返回null,结束while循环
                subscriberClass = getSuperclass(subscriberClass);
            }
        }
    }

可以看到这个方法主要是对第一步查找到的订阅者和订阅方法进行检查,因为要生成的索引类中要访问订阅类和事件,所以必须要求订阅类和Event参数是可访问的,并且Event参数必须是类或接口类型,因为EventBus的订阅方法是不支持基本类型的。

来看下可访问性检查isVisible

    private boolean isVisible(String myPackage, TypeElement typeElement) {
        // 获取修饰符
        Set<Modifier> modifiers = typeElement.getModifiers();
        boolean visible;
        if (modifiers.contains(Modifier.PUBLIC)) {
            // public的直接return true
            visible = true;
        } else if (modifiers.contains(Modifier.PRIVATE) || modifiers.contains(Modifier.PROTECTED)) {
            // private和protected的直接return false
            visible = false;
        } else {
            // 获取元素所在包名
            String subscriberPackage = getPackageElement(typeElement).getQualifiedName().toString();
            if (myPackage == null) {
                // 是否都在最外层,没有包名
                visible = subscriberPackage.length() == 0;
            } else {
                // 是否在同一包下
                visible = myPackage.equals(subscriberPackage);
            }
        }
        return visible;
    }

获取元素类型方法getParamTypeMirror

    private TypeMirror getParamTypeMirror(VariableElement param, Messager messager) {
        // 获取元素类型
        TypeMirror typeMirror = param.asType();
        // 元素类型必须得是类或接口类型
        if (typeMirror instanceof TypeVariable) {
            // 获取该类型变量的上边界,如果有extends,则返回父类,否则返回Object
            TypeMirror upperBound = ((TypeVariable) typeMirror).getUpperBound();
            // 是声明类型
            if (upperBound instanceof DeclaredType) {
                if (messager != null) {
                    messager.printMessage(Diagnostic.Kind.NOTE, "Using upper bound type " + upperBound +
                            " for generic parameter", param);
                }
                // 替换参数类型为上边界的类型
                typeMirror = upperBound;
            }
        }
        return typeMirror;
    }

createInfoIndexFile

生成Java文件:

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);
            //开始向Java文件中写入代码
            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");
            // 写入查找到的订阅者的相关代码
            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
                }
            }
        }
    }

惊呆了有没有,果然是大神,没有使用第三方框架比如javapoet,硬是一行行写出来的,佩服!

重点就在writeIndexLines方法了:

private void writeIndexLines(BufferedWriter writer, String myPackage) throws IOException {
        // 遍历methodsByClass
        for (TypeElement subscriberTypeElement : methodsByClass.keySet()) {
            // 跳过不合格的订阅者
            if (classesToSkip.contains(subscriberTypeElement)) {
                continue;
            }
            // 获取订阅类的字符串名称,格式是:包名.类名.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);
                // 生成 [new 一个SubscriberMethodInfo,并将订阅方法的相关信息写入]的代码
                writeCreateSubscriberMethods(writer, methods, "new SubscriberMethodInfo", myPackage);
                writer.write("        }));\n\n");
            } else {
                writer.write("        // Subscriber not visible to index: " + subscriberClass + "\n");
            }
        }
    }

很简单,遍历查找到的所有订阅者,然后跳过不合法的订阅者,生成写入订阅者的代码。显然,writeCreateSubscriberMethods方法中生成了写入订阅方法的代码:

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();
            // 参数类型 类名
            String eventClass = getClassString(paramElement, myPackage) + ".class";

            // 获取方法上的注解
            Subscribe subscribe = method.getAnnotation(Subscribe.class);
            List<String> parts = new ArrayList<>();
            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() + ")");
            }

        }
    }

writeLine方法只是按Java格式生成代码,无需深入了解。最后生成的代码就是上面提到的MyEventBusIndex.java

总结

注解处理器的作用其实就是提前在编译阶段就把订阅者的信息解析出来,在运行期间直接使用,极大的提高了效率和性能!ButterKnife等知名框架都用到了这种技术,掌握它还是很有必要的!