热点新闻
Android 自定义编译时注解(APT)
2023-07-14 23:13  浏览:415  搜索引擎搜索“广企汇”
温馨提示:为防找不到此信息,请务必收藏信息以备急用! 联系我时,请说明是在广企汇看到的信息,谢谢。
展会发布 展会网站大全 报名观展合作 软文发布

APT即为Annotation Processing Tool,它是javac的一个工具,中文意思为编译时注解处理器,APT可以用来在编译时扫描和处理注解,通过APT可以获取到注解和被注解对象的相关信息,在拿到这些信息后我们可以根据需求来自动的生成一些代码,省去了手动编写,注意,获取注解及生成代码都是在代码编译时候完成的,相比反射在运行时处理注解大大提高了程序性能。




image.png


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里面查看





捕获.PNG


从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目录下已生成对应文件





捕获.PNG

注解处理器核心类与函数解析

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的核心工具类





捕获.PNG


获取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代表程序元素:包,类,方法都是一种程序元素





捕获.PNG

  • VariableElement 代表一个 字段, 枚举常量, 方法或者构造方法的参数, 局部变量及 异常参数等元素
  • PackageElement 代表包元素
  • TypeElement 代表类或接口元素
  • ExecutableElement 代码方法,构造函数,类或接口的初始化代码块等元素,也包括注解类型元素

public interface Element extends javax.lang.model.AnnotatedConstruct { //返回一个TypeMirror元素的类型信息,包括包名,类(或方法,或参数) //名的类型,在生成动态代码的时候,我们往往需要知道变量/方法参数的类 //型 ,以便写入正确的类型声明 TypeMirror asType(); //返回element的类型,判断是哪种element ElementKind getKind(); //获取修饰关键字,入public static final,abstract等关键字 Set<Modifier> getModifiers(); //获取名字,不带包名 Name getSimpleName(); //getEnclosedElements Element getEnclosingElement(); //返回该元素直接包含的子元素,通常对一个PackageElement而言,它可 //以包含TypeElement;对于一个TypeElement而言,它可能包含属性 //VariableElement,方法ExecutableElement List<? extends Element> getEnclosedElements(); //获取该元素上的注解的类型信息 @Override List<? extends AnnotationMirror> getAnnotationMirrors(); //获取该元素上的注解 @Override <A extends Annotation> A getAnnotation(Class<A> annotationType); @Override <A extends Annotation> A[] getAnnotationsByType(Class<A> 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 { PACKAGE, // Declared types ENUM, CLASS, ANNOTATION_TYPE, INTERFACE, // Variables ENUM_CONSTANT, FIELD, PARAMETER, LOCAL_VARIABLE, EXCEPTION_PARAMETER, // Executables METHOD, CONSTRUCTOR, STATIC_INIT, INSTANCE_INIT, TYPE_PARAMETER, OTHER, // Constants added since initial release RESOURCE_VARIABLE, MODULE, RECORD, RECORD_COMPONENT, BINDING_VARIABLE; }

Modifier

public enum Modifier { PUBLIC, PROTECTED, PRIVATE, ABSTRACT, DEFAULT, STATIC, SEALED, NON_SEALED { public String toString() { return "non-sealed"; } }, FINAL, TRANSIENT, VOLATILE, SYNCHRONIZED, NATIVE, STRICTFP; }

TypeMirror接口

public interface TypeMirror extends javax.lang.model.AnnotatedConstruct { //返回TypeKind类型,java语言中的类型.Types包括基本类型,声明类型(类类型和接口类 //型),数组,类型变量和空类型 TypeKind getKind(); @Override List<? extends AnnotationMirror> getAnnotationMirrors(); @Override <A extends Annotation> A getAnnotation(Class<A> annotationType); @Override <A extends Annotation> A[] getAnnotationsByType(Class<A> annotationType); }

public enum TypeKind { BOOLEAN, BYTE, SHORT, INT, LONG, CHAR, FLOAT, DOUBLE, VOID, NONE, NULL, ARRAY, DECLARED, ERROR, TYPEVAR, WILDCARD, PACKAGE, EXECUTABLE, OTHER, UNIOn, INTERSECTION, 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

发布人:fffc****    IP:139.201.89.***     举报/删稿
展会推荐
让朕来说2句
评论
收藏
点赞
转发