在 JDK 1.5 之后,Java 语言提供了对注解(Annotation)的支持,这些注解和普通的 Java 代码一样,是在运行期间发挥作用的。在JDK 1.6 中实现了JSR-269 规范。提供了一组插入式注解处理器的标准 API 在编译期间对注解进行处理,我们可以把它看做是一组编译器的插件,在这些插件里面,可以读取,修改,添加抽象语法书中的任意元素。
编译注解主要是在编译过程中,生成必要的文件,这样在运行时调用,就不需要再通过大量的反射(低效)来进行操作。
这种形式大大提高了注解在运行时的效率,但同时也增加了编译的时间。当然,编译时间的长短我们无所谓啦。
APT(Annotation Processing Tool)是javac内置的工具,用于在编译时期扫描和处理注解信息。它对源代码文件进行检测找出其中的Annotation,根据注解自动生成代码(调用注解处理器的Process方法生成源文件)。 Annotation处理器在处理Annotation时可以根据源文件中的Annotation生成额外的源文件和其它的文件(文件具体内容由Annotation处理器的编写者决定),APT还会编译生成的源文件和原来的源文件,将它们一起生成class文件。
AbstractProcessor是javax.annotation.processing包下的一个抽象类。但是,Android平台是基于OpenJDK的,而OpenJDK中不包含Annotation Processor的相关代码。
常用方法名 | 作用 |
---|---|
init(…) | 初始化工作 |
getSupportedSourceVersion() | 指定当前注解器使用的Jdk版本 |
getSupportedAnnotationTypes() | 指出注解处理器 支持处理哪种注解 |
process(…) | APT扫描到注解后调用方法 |
做一些初始化工作,注释处理工具会调用此方法,并传过来processingEnv参数。
通过ProcessingEnvironment 可以获取到很多可操作类,如:
操作类 |
---|
Elements es = processingEnvironment.getElementUtils(); |
Filer filer = processingEnvironment.getFiler(); |
Types types = processingEnvironment.getTypeUtils(); |
Messager messager = processingEnvironment.getMessager(); |
SourceVersion version = processingEnvironment.getSourceVersion(); |
messager.printMessage(Diagnostic.Kind.ERROR,"类出现错误",typeElement);
指定当前注解器使用的Jdk版本
在 jdk1.7 中,我们可以使用注解 @SupportedSourceVersion()代替
指出注解处理器 处理哪种注解
在 jdk1.7 中,我们可以使用注解 @SupportedAnnotationTypes()代替
常用方法名 | 含义 |
---|---|
errorRaised() | 如果在以前的处理 round 中发生错误,则返回 true;否则返回 false。 |
getElementsAnnotatedWith(…) | 返回使用给定注释类型注释的元素。 |
子类 | 说明 |
---|---|
ExecutableElement | 表示某个类或接口的方法、构造方法或初始化程序(静态或实例),包括注释类型元素。对应@Target(ElementType.METHOD) @Target(ElementType.CONSTRUCTOR) |
TypeElement | 表示一个类或接口程序元素。提供对有关类型及其成员的信息的访问。注意,枚举类型是一种类,而注释类型是一种接口。对应@Target(ElementType.TYPE) |
VariableElement | 表示一个字段、enum 常量、方法或构造方法参数、局部变量或异常参数。对应 @Target(ElementType.LOCAL_VARIABLE) |
PackageElement | 表示一个包程序元素。提供对有关包及其成员的信息的访问。对应@Target(ElementType.PACKAGE) |
TypeParameterElement | 表示一般类、接口、方法或构造方法元素的形式类型参数。对应@Target(ElementType.PARAMETER) |
asType()
返回此元素定义的类型 TypeMirror。
getEnclosingElement()
返回封装此元素(非严格意义上)的最里层元素。
getKind()
返回此元素的类型。
getModifiers()
返回此元素的修饰符,不包括注释。
getSimpleName()
返回此元素的简单(未限定)名称。
两种方法:
1.使用apt。
一个个人写的第三方jar,在谷歌出了annotationProcessor之后,停止了更新,并建议大家使用官方出品。所以这里咱们不再了解了。
2.官方出品,使用简单方便。
只需导入'com.google.auto.service:auto-service:1.0-rc3'
jar包即可
在自定义的AbstractProcessor上使用 @AutoService(Processor.class)(com.google.auto相关包)注解,便可自动为 JAVA Processor 生成 META-INF 信息。
然后,在调用时,使用annotationProcessor 引用即可
例如:
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.android.support:appcompat-v7:27.1.1'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
annotationProcessor project(':processer')
implementation project(':anna')
// implementation project(':processer') //不行
}
在onprocess方法中执行
一般处理器处理逻辑:
有多种方法可以实现生成java文件:
1.直接使用字符串拼接:
/**
* 字符串拼接形式 生成文件
* @param className 类名
* @param output 注解名
*/
private void createFile(String className, String output) {
StringBuilder cls = new StringBuilder();
cls.append("package apt;\n\npublic class ")
.append(className)
.append(" {\n public static void main(String[] args) {\n")
.append(" System.out.println(\"")
.append(output)
.append("\");\n }\n}");
try {
JavaFileObject sourceFile = filer.createSourceFile("apt." + className);
Writer writer = sourceFile.openWriter();
writer.write(cls.toString());
writer.flush();
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
2.使用javapoet框架,更简单。
javapoet的使用,请看下一篇文章。
找到项目目录下的gradle.properties 文件,打开:
将原先的 org.gradle.jvmargs=-Xmx1536m注掉。
改为
org.gradle.daemon=true
org.gradle.jvmargs=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005
当然,最后的5005端口是可以修改的
有时候会出现:
Error running “AnnotationProcessor”
Unable to open debugger port{localhost:5005}:java.net.ConnectException”Connection refused:connect”的错误,这个时候把端口号换下改成其他的试试(有可能是占用了)。
在Terminal控制台中输入gradlew clean assembleDebug即可
然后,每次需要调试注解处理器时候,只需要选中之前配置的,点击
,然后,再在Terminal控制台中输入gradlew clean assembleDebug即可(或者点击rebuild)
再次感谢以下朋友的分享文章:
比较全面的介绍编译时注解的使用, 自定义运行时注解、编译时注解[ButterKnife原理探析]
Java编译时注解处理器(Annotation Processor)详解
Android 自定义注解详解