美文网首页Android APTAPP & program
Android 自定义编译时注解(APT)

Android 自定义编译时注解(APT)

作者: 三十五岁养老 | 来源:发表于2022-06-19 21:52 被阅读0次

    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 {
    
        /** 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 extends Annotation> A getAnnotation(Class<A> annotationType);
        @Override
        <A extends Annotation> A[] getAnnotationsByType(Class<A> 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

    相关文章

      网友评论

        本文标题:Android 自定义编译时注解(APT)

        本文链接:https://www.haomeiwen.com/subject/fhulvrtx.html