Android-APT APT (Annotation Processing Tool) 发生在 Javac 编译成 Class 文件之后、Class 编码成 Dex 之前。Processor 处理的输入数据是 Javac 编译后的 Class 信息,因此 APT 的执行流程在 Javac 之后,对于 Android 开发中多 Module 的场景,由于每一个 Module 都是并行编译的,只有在产生依赖关系时才会具有先后顺序,因此只要 Module 在 Gradle 中声明了 annotationProcessor
,则每个声明了的 Module 都会独立在 Javac 之后调用对应的 Processor 进行处理。因此 Processor 的代码可能会被调用多次,而且每一次调用时的环境都是独立、互不影响的。
1. 自定义APT 创建自定义的 Annotation Processor 有两个前提:
Processor Module 必须是纯 Java / Kotlin Library
Module 只需要通过 annotationProcessor xxx
即可应用 APT,不需要也不能依赖 Processor Module
因此 APT 的整体结构一般分为如下几个部分:
AnnotationModule:用于存放自定义注解类,并且为纯 Java / Kotlin Library(应为需要被 Processor Module 依赖,而后者也是纯 Java / Kotlin Library);
ProcessorModule:用于存放扫描自定义注解的 Processor,依赖 AnnotationModule,同样为纯 Java / Kotlin Library;
APIModule:用于对外提供业务功能,因为 APT 的作用是根据注解完成一些自动化逻辑,因此通常还会提供一些接口,当然如果是一个纯粹服务于编译流程的 APT,可以没有该 Module。同样依赖 AnnotationModule,可以是纯 Java / Kotlin Library、也可以是一个 Android Library,。
1.1 创建注解类 1 2 3 4 5 6 7 8 9 @Documented @Inherited @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface DemoAnnotation { int value () default 0 ; }
1.2 创建Processor 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 70 71 72 73 74 @SupportedOptions({"org.gradle.annotation.processing.aggregating"}) public class DemoProcessor extends AbstractProcessor { private Filer filer; private Messager messager; private Types typeUtils; private Elements elements; @Override public synchronized void init (ProcessingEnvironment processingEnvironment) { super .init(processingEnvironment); filer = processingEnvironment.getFiler(); messager = processingEnvironment.getMessager(); typeUtils = processingEnvironment.getTypeUtils(); elements = processingEnvironment.getElementUtils(); } @Override public SourceVersion getSupportedSourceVersion () { return SourceVersion.latestSupported(); } @Override public Set<String> getSupportedOptions () { return Collections.singleton("org.gradle.annotation.processing.aggregating" ); } @Override public Set<String> getSupportedAnnotationTypes () { return Colletions.singleton(DemoAnnotation.class.getCanonicalName()); } @Override public boolean process (Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) { return false ; } }
1.3 注册Processor 在 Android 中使用自定义注解,有两种注册 Processor 的方式:
AutoService 自动注册
META-INF 中手动注册
假设 Processor 所在 Module 为 module_processor
。
1.3.1 AutoService自动注册 借助 Google 推出的 AutoService 工具自动完成 Processor 的注册:
1 2 3 4 5 6 7 8 9 implementation 'com.google.auto.service:auto-service:1.0' annotationProcessor 'com.google.auto.service:auto-service:1.0' @AutoService(Processor.class) public class DemoProcessor extends AbstractProcessor { ...... }
实际上就是用 @AutoService
的 Processor 动态创建了 DemoProcessor 的注册信息完成注册的。
通过 META-INF 手动注册,与 Java 中的注册方式是一样的:
(1)在 Processor 所在 Module 下创建注册信息文件:
1 2 3 4 5 6 7 8 9 10 11 module_processor/src/main/resources/META-INF/services/javax.annotation.processing.Processor // 该文件目录结构如下: module_processor └─ src └─ main └─ java └─ resources └─ META-INF └─ services └─ javax.annotation.processing.Processor
(2)在 javax.annotation.processing.Processor
中声明 Processor 的全路径,例如:
重新 Build 即可发现 DemoProcessor 已经可以正确处理。
1.4 使用Processor 当一个 Processor 成功被注册后,默认情况下只会处理 Processor 所在 Module 的注解,如果其他 Module 也需要使用该 Processor,则需要在 Gradle 中声明:
1 2 annotationProcessor project(path: ':module_processor' )
如果想对 Gradle 编译时断点调试,需要新增单独的调试任务:
(1)在 Run/Debug Configurations
中新增一个 Remote
或 Remote JVM Debug
任务,然后再右侧栏将目标地址选择为本地的某个端口,例如 8888:
1 2 3 Name: RemoteDebug(可自定义) ...... Host: localhost Port: 8888
(2)修改项目根目录下的 gradle.properties
,为 Gradle 添加 2 条 Debug 配置:
1 2 3 4 -Dorg.gradle.debug=true org.gradle.jvmargs=-agentlib:jdwp=transport=dt_socket,server=y,suspend =n,address=8888
(3)重新进入 Run/Debug Configurations
中查看新增的 Remote Configuration,可以在其中的 Command line arguments for remote JVM
看到新增的 Gradle 配置参数。
(4)在 Run/Debug Configurations
中选中新增的 Remote Configuration,执行 Debug ,然后再执行编译任务(Rebuild, Assemble...),即可进入断点。
2. Processor开启增量编译 通过上述方式成功注册一个 Processor 后,在 Build 时 Gradle 可能会报如下警告:
1 2 The following annotation processors are not incremental: jetified-module_processor.jar (project :module_processor). Make sure all annotation processors are incremental to improve your build speed.
大致含义为 Processor 每次都是全量编译的,尽可能确保采用增量编译以提高编译速度。
查阅相关资料后发现,增量编译选项需要在 Processor 的注册信息中指定,对于上文中的两种注册方式:
如果使用 AutoService 自动注册,Auto-Service 在 1.0-rc6 版本中才支持增量编译。
如果使用手动注册,则需要从 Java 原生模式注册信息修改为 Gradle 注册信息。
2.1 AutoService开启增量编译 将 AutoService 的依赖以及对应的 annotationProcessor 升级到 1.0-rc6
或以上即可。
2.2 手动注册开启增量编译 手动注册的 Processor 要开启增量编译,则需要切换为 Gradle 模式的注册信息。
(1)在 Processor 所在 Module 创建 Gradle 模式的注册信息文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 module_processor/src/main/resources/META-INF/gradle/incremental.annotation.processors // Java 原生模式和 Gradle 模式的注册信息文件结构对比: module_processor └─ src └─ main └─ java └─ resources └─ META-INF └─ gradle └─ incremental.annotation.processors (Gradle 模式注册信息文件) └─ services └─ javax.annotation.processing.Processor (Java 原生模式注册信息文件)
两种模式的注册信息文件可以共存,Gradle 编译时会优先选择 Gradle 模式的注册信息。
(2)在 incremental.annotation.processors
中声明支持增量编译的注册信息:
1 priv.demo.DemoProcessor,isolating
重新 Build,Gradle 相关警告日志已经消除。
此外,Gradle 模式注册信息还支持在 Processor 代码中决定编译类型,只需要在 incremental.annotation.processors
中的注册信息声明为 dynamic
类型,然后在 Processor#getSupportedOptions(...)
中指定具体的编译类型即可:
1 2 3 4 5 6 7 8 9 10 11 12 priv.demo.DemoProcessor,dynamic public class DemoProcessor extends AbstractProcessor { @Override public Set<String> getSupportedOptions () { return Collections.singleton("org.gradle.annotation.processing.aggregating" ); } ...... }
3. APT过滤元素 一个 Processor 可以同时处理多个自定义注解,因此过滤 Class 分为两步:
确定需要过滤的注解
获取并处理包含注解的元素(类、方法、属性等)。
3.1 选择需要过滤的注解 1 2 3 4 5 6 7 8 9 10 11 12 13 public class DemoProcessor extends AbstractProcessor { @Override public Set<String> getSupportedAnnotationTypes () { Set<String> routerAnnotationTypeSet = new HashSet<>(); routerAnnotationTypeSet.add(DemoAnnotation.class.getCanonicalName()); routerAnnotationTypeSet.add("priv.demo.DemoAnnotation" ); return routerAnnotationTypeSet; } }
3.2 根据注解过滤需要处理的元素 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 public class DemoProcessor extends AbstractProcessor { private Elements elementUtils; @Override public synchronized void init (ProcessingEnvironment processingEnvironment) { super .init(processingEnvironment); elementUtils = processingEnvironment.getElementUtils(); } @Override public boolean process (Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) { if (set == null || set.isEmpty() || roundEnvironment == null ) { return false ; } if (roundEnvironment != null && roundEnvironment.processingOver()) { } Set<? extends Element> demoAnnotatedClassSet = roundEnvironment.getElementsAnnotatedWith(DemoAnnotation.class); TypeElement demoAnnotationClassElement = elementUtils.getTypeElement("priv.demo.DemoAnnotation" ); Set<? extends Element> demoAnnotatedClassSet = roundEnvironment.getElementsAnnotatedWith(demoAnnotationClassElement); for (Element annotatedElement : demoAnnotatedClassSet) { processElement(annotatedElement); } return true ; } private void processElement (RoundEnvironment roundEnvironment, Element annotatedElement) { if (annotatedElement == null ) { return ; } if ((annotatedElement instanceof TypeElement) && annotatedElement.getKind() == ElementKind.CLASS) { } if (annotatedElement.getModifiers().contains(Modifier.PUBLIC)) { } TypeElement annotatedTypeElement = ((TypeElement) annotatedElement); for (TypeMirror eachTypeMirror : annotatedTypeElement.getInterfaces()) { TypeMirror superInterfaceTypeMirror = elementUtils.getTypeElement("priv.demo.IDemoInterface" ).asType(); boolean isSubInterface = typeUtils.isSubtype(eachTypeMirror, superInterfaceTypeMirror); } } }
需要注意的是,当使用全限定类名的方式(elementUtils.getTypeElement("priv.demo.DemoAnnotation")
)获取注解类对应的 TypeElement 对象时,仅当应用了 Processor 的 Module 可以访问注解类时返回值才不为 null,但与 Processor 所在 Module 本身是否可以访问注解类无关,例如:
1 2 3 - module_annotation:不依赖任何 Module - module_processor:不依赖任何 Module - module_app:通过 annotationProcessor project(path: ':module_processor' ) 应用 APT
此时如果 module_app 具有 module_annotation 的依赖,则 elementUtils.getTypeElement("priv.demo.DemoAnnotation")
可以正确返回 DemoAnnotation 的 TypeElement 对象,否则将返回 null,与 module_processor 是否具有 module_annotation 的依赖无关。
4. APT生成类 成功注册一个 Processor 后,就能通过对应的注解获取相关信息,并根据这些信息动态生成类。生成类文件时,可以通过 JavaPoet 等工具生成,也可以自行拼接代码文本 String 内容后直接写入文件。
4.1 在同一个Module中生成类 仍以 DemoAnnotation
为例:
1 2 3 4 5 6 7 8 @Documented @Inherited @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface DemoAnnotation { int value () default 0 ; }
以 JavaPoet 为例,获取所有添加了 @DemoAnnotation
注解的类,并在与之相同的 Module 的 Build 目录下生成一个名为 {ClassName}_{AnnotationValue}
新的类:
例如:
@DemoAnnotation class DemoClassA {}
将生成一个名为 DemoClassA_0
的类(因为 DemoAnnotation#value()
的默认值为 0);
@DemoAnnotation(123) class DemoClassB {}
将生成一个名为 DemoClassB_123
的类。
使用 JavaPoet 需要添加依赖:implementation 'com.squareup:javapoet:1.13.0'
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 public class DemoProcessor extends BaseRouterProcessor { private void processElement (Element annotatedElement) { if ((eachElement instanceof TypeElement) && annotatedElement.getKind() == ElementKind.CLASS) { String packageName = annotatedElement.getEnclosingElement().toString(); String className = annotatedElement.getSimpleName().toString(); } DemoAnnotation demoAnnotation = annotatedElement.getAnnotation(DemoAnnotation.class); int value = demoAnnotation.value(); List<? extends AnnotationMirror> annotationMirrorList = element.getAnnotationMirrors(); try { JavaFile.builder(packageName, TypeSpec.classBuilder(className + "_" + value) .addJavadoc("DO NOT edit this file !!!" ) .addModifiers(Modifier.PUBLIC, Modifier.FINAL) .build()) .build() .writeTo(filer); messager.printMessage(Kind.NOTE, "Generated: " + packageName + "." + className); } catch (Exception e) { messager.printMessage(Kind.ERROR, "Failed: " + packageName + "." + className); throw new RuntimeException(e); } } }
其中在 Processor#process(...)
中,通过 JavaPoet 生成类的最后一步,调用的是 writeTo(filer)
,而 filer
是在 Processor#init(...)
中通过运行环境获取的,可以理解为:每一个 Module 在 Javac 之后,Processor 都会被当前 Module 调用,因此 Filer 中的信息都是针对当前 Module 的,所以 writeTo(filer)
将会在当前 Module 下生成文件。
4.2 在任意目录生成类 通过查看 JavaPoet 和 Filer 的源码可以知道,writeTo(filer)
实际上也是通过 Filer 获取了当前 Module 的路径信息,只不过该路径信息是相对当前 Module 的,但实际上 JavaPoet 的 writeTo(...)
是允许直接指定任意路径的,只需要传入对应路径的 Path 对象即可:
1 2 3 4 5 6 File generatedFile = new File("/DemoProject/" + className + ".java" ); JavaFile.builder(packageName, TypeSpec.classBuilder(className) .build()) .build() .writeTo(generatedFile.toPath());
参考文献