Android 自定义编译时注解(APT)
APT即为Annotation Processing Tool,它是javac的一个工具,中文意思为编译时注解处理器,APT可以用来在编译时扫描和处理注解,通过APT可以获取到注解和被注解对象的相关信息,在拿到这些信息后我们可以根据需求来自动的生成一些代码,省去了手动编写,注意,获取注解及生成代码都是在代码编译时候完成的,相比反射在运行时处理注解大大提高了程序性能。
apt是在生成.class文件之前执行,故在apt里面无法通过反射获取其他类的方法,因为反射是通过ClassLoader将Class文件加载到JVM中,在内存中进行管理。
注解处理器是运行它自己的虚拟机JVM中,javac启动一个完整Java虚拟机来运行注解处理器,
自定义编译注解
工程结构:
- annotation (注解和处理器生成代码相关,有的喜欢将注解和处理器分成两个包)
- app
annotation 模块
- 新建java lib 命名annotation
- build.gradle 导入依赖
dependencies {
implementation 'com.squareup:javapoet:1.13.0'
implementation 'com.google.auto.service:auto-service:1.0-rc6'
annotationProcessor 'com.google.auto.service:auto-service:1.0-rc6'
}
- 新建java类定义注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface NativeAnnotation {
String path() default "";
}
- 新建Processor
@AutoService(Processor.class)
public class NativeProcessor extends AbstractProcessor {
@Override
public boolean process(Set extends TypeElement> annotations, RoundEnvironment roundEnv) {
return false;
}
}
app模块
- build.gradle 导入依赖
implementation project(':annotation')
annotationProcessor project(':annotation')
- 使用注解
@NativeAnnotation(path = "111")
public class MainActivity extends AppCompatActivity {}
在处理器里面加入log 确认处理器有没有生效
@AutoService(Processor.class)
public class NativeProcessor extends AbstractProcessor {
@Override
public boolean process(Set extends TypeElement> annotations, RoundEnvironment roundEnv) {
processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING," test NativeProcessor");
return false;
}
}
注解解释器输出的日志在build log里面查看
从log中可以看出 解释器已生效, 在process方法中可以写自己想要的逻辑,比如生成java文件
@Override
public boolean process(Set extends TypeElement> annotations, RoundEnvironment roundEnv) {
processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING," test NativeProcessor");
ClassName className = ClassName.bestGuess("com.example.Useful");
TypeSpec.Builder userTypeSpec = TypeSpec.classBuilder(className)
.addModifiers(Modifier.PUBLIC);
// private int id = 0;
FieldSpec idFieldSpec = FieldSpec.builder(int.class, "id", Modifier.PRIVATE)
.initializer("0").build();
userTypeSpec.addField(idFieldSpec);
userTypeSpec.addJavadoc("注释");
JavaFile javaFile = JavaFile.builder("com.example",userTypeSpec.build()).build();
try {
javaFile.writeTo(processingEnv.getFiler());
} catch (IOException e) {
e.printStackTrace();
}
return true;
}
在build目录下已生成对应文件
注解处理器核心类与函数解析
AbstractProcessor
AbstractProcessor抽象类是实现了Processor接口,具体类变量和函数解析如下:
-
init(ProcessingEnvironment env):init()方法会被注解处理工具调用,并输入ProcessingEnviroment参数。ProcessingEnviroment提供很多有用的工具类Elements, Types和Filer。
-
process(Set extends TypeElement> annotations, RoundEnvironment env): 这相当于每个处理器的主函数main()。你在这里写你的扫描、评估和处理注解的代码,以及生成Java文件。输入参数RoundEnviroment,可以让你查询出包含特定注解的被注解元素。
-
getSupportedAnnotationTypes(): 这里你必须指定,这个注解处理器是注册给哪个注解的。注意,它的返回值是一个字符串的集合,包含本处理器想要处理的注解类型的合法全称。换句话说,你在这里定义你的注解处理器注册到哪些注解上。
-
getSupportedSourceVersion(): 用来指定你使用的Java版本。通常这里返回SourceVersion.latestSupported()。然而,如果你有足够的理由只支持Java 6的话,你也可以返回SourceVersion.RELEASE_6。我推荐你使用前者。
ProcessingEnvironment
ProcessingEnvironment对象是apt的核心工具类
获取Elements的类型
processingEnv.getElementUtils().getTypeElement(type).asType()
输出调试日志
processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING,"
test NativeProcessor");
判断此元素的类型,做相关安全校验之类的
private boolean isSubtype(Element typeElement, String type) {
return processingEnv.getTypeUtils().isSubtype(typeElement.asType(),
processingEnv.getElementUtils().getTypeElement(type).asType());
}
返回用来创建类或者辅助文件的filer
JavaFile javaFile = JavaFile.builder("com.example",userTypeSpec.build()).build();
try {
javaFile.writeTo(processingEnv.getFiler());
} catch (IOException e) {
e.printStackTrace();
}
RoundEnvironment
public interface RoundEnvironment {
boolean processingOver();
//上一轮注解处理器是否产生错误
boolean errorRaised();
//返回上一轮注解处理器生成的根元素
Set extends Element> getRootElements();
//返回包含指定注解类型的元素的集合
Set extends Element> getElementsAnnotatedWith(TypeElement a);
//返回包含指定注解类型的元素的集合
Set extends Element> getElementsAnnotatedWith(Class extends Annotation> a);
}
Element
element表示一个静态的,语言级别的构件。而任何一个结构化文档都可以看作是由不同的element组成的结构体,对java源文件来说
package com.closedevice; //PackageElement
public class Main{ //TypeElement
private int x; //VariableElement
private Main(){ //ExecuteableElement
}
private void print( //ExecuteableElement
int msg
){ //VariableElement
}
}
Element代表程序元素:包,类,方法都是一种程序元素
- VariableElement 代表一个 字段, 枚举常量, 方法或者构造方法的参数, 局部变量及 异常参数等元素
- PackageElement 代表包元素
- TypeElement 代表类或接口元素
- ExecutableElement 代码方法,构造函数,类或接口的初始化代码块等元素,也包括注解类型元素
public interface Element extends javax.lang.model.AnnotatedConstruct {
//返回一个TypeMirror元素的类型信息,包括包名,类(或方法,或参数)
//名的类型,在生成动态代码的时候,我们往往需要知道变量/方法参数的类
//型 ,以便写入正确的类型声明
TypeMirror asType();
//返回element的类型,判断是哪种element
ElementKind getKind();
//获取修饰关键字,入public static final,abstract等关键字
Set getModifiers();
//获取名字,不带包名
Name getSimpleName();
//getEnclosedElements
Element getEnclosingElement();
//返回该元素直接包含的子元素,通常对一个PackageElement而言,它可
//以包含TypeElement;对于一个TypeElement而言,它可能包含属性
//VariableElement,方法ExecutableElement
List extends Element> getEnclosedElements();
//获取该元素上的注解的类型信息
@Override
List extends AnnotationMirror> getAnnotationMirrors();
//获取该元素上的注解
@Override
A getAnnotation(Class annotationType);
@Override
A[] getAnnotationsByType(Class annotationType);
}
以上是Element的方法,同时它的子类有自己的方法
public interface ExecutableElement extends Element, Parameterizable {
//用于获取方法的参数元素,每个元素是一个VariableElement
List extends VariableElement> getParameters();
//获取方法元素的返回值,返回类型TypeMirror表示
TypeMirror getReturnType()
}
public interface VariableElement extends Element {
//如果属性变量被final修饰,则可以使用该方法获取它的值
Object getConstantValue();
}
public interface TypeElement extends Element, Parameterizable, QualifiedNameable {
//获取类全限定名
Name getQualifiedName();
}
ElementKind
public enum ElementKind {
/** A package. */
PACKAGE,
// Declared types
/** An enum class. */
ENUM,
/**
* A class not described by a more specific kind (like {@code
* ENUM} or {@code RECORD}).
*/
CLASS,
/** An annotation interface. (Formerly known as an annotation type.) */
ANNOTATION_TYPE,
/**
* An interface not described by a more specific kind (like
* {@code ANNOTATION_TYPE}).
*/
INTERFACE,
// Variables
/** An enum constant. */
ENUM_CONSTANT,
/**
* A field not described by a more specific kind (like
* {@code ENUM_CONSTANT}).
*/
FIELD,
/** A parameter of a method or constructor. */
PARAMETER,
/** A local variable. */
LOCAL_VARIABLE,
/** A parameter of an exception handler. */
EXCEPTION_PARAMETER,
// Executables
/** A method. */
METHOD,
/** A constructor. */
CONSTRUCTOR,
/** A static initializer. */
STATIC_INIT,
/** An instance initializer. */
INSTANCE_INIT,
/** A type parameter. */
TYPE_PARAMETER,
/**
* An implementation-reserved element. This is not the element
* you are looking for.
*/
OTHER,
// Constants added since initial release
/**
* A resource variable.
* @since 1.7
*/
RESOURCE_VARIABLE,
/**
* A module.
* @since 9
*/
MODULE,
/**
* A record class.
* @since 16
*/
RECORD,
/**
* A record component of a {@code record}.
* @since 16
*/
RECORD_COMPONENT,
/**
* A binding variable in a pattern.
* @since 16
*/
BINDING_VARIABLE;
}
Modifier
public enum Modifier {
/** The modifier {@code public} */ PUBLIC,
/** The modifier {@code protected} */ PROTECTED,
/** The modifier {@code private} */ PRIVATE,
/** The modifier {@code abstract} */ ABSTRACT,
/**
* The modifier {@code default}
* @since 1.8
*/
DEFAULT,
/** The modifier {@code static} */ STATIC,
/**
* The modifier {@code sealed}
* @since 17
*/
SEALED,
/**
* The modifier {@code non-sealed}
* @since 17
*/
NON_SEALED {
public String toString() {
return "non-sealed";
}
},
/** The modifier {@code final} */ FINAL,
/** The modifier {@code transient} */ TRANSIENT,
/** The modifier {@code volatile} */ VOLATILE,
/** The modifier {@code synchronized} */ SYNCHRONIZED,
/** The modifier {@code native} */ NATIVE,
/** The modifier {@code strictfp} */ STRICTFP;
}
TypeMirror接口
public interface TypeMirror extends javax.lang.model.AnnotatedConstruct {
//返回TypeKind类型,java语言中的类型.Types包括基本类型,声明类型(类类型和接口类
//型),数组,类型变量和空类型
TypeKind getKind();
@Override
List extends AnnotationMirror> getAnnotationMirrors();
@Override
A getAnnotation(Class annotationType);
@Override
A[] getAnnotationsByType(Class annotationType);
}
public enum TypeKind {
/**
* The primitive type {@code boolean}.
*/
BOOLEAN,
/**
* The primitive type {@code byte}.
*/
BYTE,
/**
* The primitive type {@code short}.
*/
SHORT,
/**
* The primitive type {@code int}.
*/
INT,
/**
* The primitive type {@code long}.
*/
LONG,
/**
* The primitive type {@code char}.
*/
CHAR,
/**
* The primitive type {@code float}.
*/
FLOAT,
/**
* The primitive type {@code double}.
*/
DOUBLE,
/**
* The pseudo-type corresponding to the keyword {@code void}.
* @see NoType
*/
VOID,
/**
* A pseudo-type used where no actual type is appropriate.
* @see NoType
*/
NONE,
/**
* The null type.
*/
NULL,
/**
* An array type.
*/
ARRAY,
/**
* A class or interface type.
*/
DECLARED,
/**
* A class or interface type that could not be resolved.
*/
ERROR,
/**
* A type variable.
*/
TYPEVAR,
/**
* A wildcard type argument.
*/
WILDCARD,
/**
* A pseudo-type corresponding to a package element.
* @see NoType
*/
PACKAGE,
/**
* A method, constructor, or initializer.
*/
EXECUTABLE,
/**
* An implementation-reserved type.
* This is not the type you are looking for.
*/
OTHER,
/**
* A union type.
*
* @since 1.7
*/
UNION,
/**
* An intersection type.
*
* @since 1.8
*/
INTERSECTION,
/**
* A pseudo-type corresponding to a module element.
* @see NoType
* @since 9
*/
MODULE
}
代码示例
获取一个类注解的值,并且获取类里面的方法
- 使用注解
@NativeAnnotation(path = " path hahaha")
public class test {
public native int nativeInit(Fragment i, int j, String[] strings,
ArrayList arrayList);
}
- process书写逻辑
public boolean process(Set extends TypeElement> annotations, RoundEnvironment roundEnv) {
//找出含有NativeAnnotation注解元素
Set extends Element> elements = roundEnv.getElementsAnnotatedWith(NativeAnnotation.class);
for (Element element : elements) {
//获取element元素上的注解
NativeAnnotation aah = element.getAnnotation(NativeAnnotation.class);
//获取注解的值
String path = aah.path();
processingEnv.getMessager().printMessage(
Diagnostic.Kind.WARNING," value path : " + path);
//判断是否是类注解
if (element.getKind() == ElementKind.CLASS) {
TypeElement typeElement = (TypeElement) element;
//获取全类名
String className = typeElement.getQualifiedName().toString();
processingEnv.getMessager().printMessage(
Diagnostic.Kind.WARNING," className: " + className);
//获取类里面的元素
List extends Element> elements1 = typeElement.getEnclosedElements();
for (Element element1 : elements1) {
//判断元素是否是方法
if (element1.getKind() == ElementKind.METHOD) {
ExecutableElement executableElement = (ExecutableElement)element1;
//打印方法名
processingEnv.getMessager().printMessage
(Diagnostic.Kind.WARNING," method : "
+ executableElement.getSimpleName());
//打印方法返回值
processingEnv.getMessager().printMessage
(Diagnostic.Kind.WARNING," return : "
+ executableElement.getReturnType().toString());
//打印方法修饰符
processingEnv.getMessager().printMessage
(Diagnostic.Kind.WARNING," Modifiers : "
+ executableElement.getModifiers().toString());
//获取方法参数
List extends VariableElement> variableElements = executableElement.getParameters();
for (VariableElement element2 : variableElements) {
//打印参数名称
processingEnv.getMessager().printMessage
(Diagnostic.Kind.WARNING," Parame name: "
+ element2.getSimpleName());
//打印参数类型
processingEnv.getMessager().printMessage
(Diagnostic.Kind.WARNING," Parame TypeKind : "
+ element2.asType().getKind().name());
//打印参数类型
processingEnv.getMessager().printMessage
(Diagnostic.Kind.WARNING," Parame type : "
+ element2.asType().toString());
}
}
}
}
}
}
- 运行结果
value path : path hahaha
����: className: com.example.annotationjnicheck.test
����: method : nativeInit
����: return : int
����: Modifiers : [public, native]
����: Parame name: i
����: Parame TypeKind : DECLARED
����: Parame type : android.app.Fragment
����: Parame name: j
����: Parame TypeKind : INT
����: Parame type : int
����: Parame name: strings
����: Parame TypeKind : ARRAY
����: Parame type : java.lang.String[]
����: Parame name: arrayList
����: Parame TypeKind : DECLARED
����: Parame type : java.util.ArrayList
参考链接:
https://blog.csdn.net/heng615975867/article/details/105072317/
http://www.360doc.com/showweb/0/0/1036633837.aspx
共有 0 条评论