Android APT基础和实践

Android APT技术说明

1. Element和TypeMirror

1.1 Element

Element 代表程序的元素,在注解处理过程中,编译器会扫描所有的Java源文件,并将源码中的每一个部分都看作特定类型的 Element

package com.example;        // PackageElement

import java.util.List;

public class Sample         // TypeElement
        <T extends List> {
    
      // TypeParameterElement

    private int num;        // VariableElement
    String name;            // VariableElement

    public Sample() {
    
    }      // ExecuteableElement

    public void setName(    // ExecuteableElement
            String name     // VariableElement
            ) {
    
    }
}

Element接口方法如下:

public interface Element extends AnnotatedConstruct{
    
    
    /**
     * 返回此元素定义的类型
     * 例如,对于一般类元素 C<N extends Number>,返回参数化类型 C<N>
     */
    TypeMirror asType();

    /**
     * 返回此元素的种类:包、类、接口、方法、字段...,如下枚举值
     * PACKAGE, ENUM, CLASS, ANNOTATION_TYPE, INTERFACE, ENUM_CONSTANT, FIELD, PARAMETER, LOCAL_VARIABLE, EXCEPTION_PARAMETER,
     * METHOD, CONSTRUCTOR, STATIC_INIT, INSTANCE_INIT, TYPE_PARAMETER, OTHER, RESOURCE_VARIABLE;
     */
    ElementKind getKind();

      /**
     * 返回此元素的修饰符,如下枚举值
     * PUBLIC, PROTECTED, PRIVATE, ABSTRACT, DEFAULT, STATIC, FINAL,
     * TRANSIENT, VOLATILE, SYNCHRONIZED, NATIVE, STRICTFP;
     */
    Set<Modifier> getModifiers();


    /**
     * 返回此元素的简单名称,例如
     * 类型元素 java.util.Set<E> 的简单名称是 "Set";
     * 如果此元素表示一个未指定的包,则返回一个空名称;
     * 如果它表示一个构造方法,则返回名称 "<init>";
     * 如果它表示一个静态初始化程序,则返回名称 "<clinit>";
     * 如果它表示一个匿名类或者实例初始化程序,则返回一个空名称
     */
    Name getSimpleName();


     /**
     * 返回封装此元素的最外层元素。比如TypeElement 返回packageelemnet
     * ExecutableElement返回typeElement,VariableElement返回TypeElement(全局Field)或者
     * ExecutableElement(局部Field)
     * 如果此元素的声明在词法上直接封装在另一个元素的声明中,则返回那个封装元素;
     * 如果此元素是顶层类型,则返回它的包;
     * 如果此元素是一个包,则返回 null;
     * 如果此元素是一个泛型参数,则返回 null.
     */
    Element getEnclosingElement();

     /**
     * 返回此元素直接封装的子元素,与getEnclosingElemen()相反,packageElement返回
     * TypeElement,TypeElement返回ExecutableElement和
     */
    List<? extends Element> getEnclosedElements();

    /**
     * 返回直接存在于此元素上的注解
     * 要获得继承的注解,可使用 getAllAnnotationMirrors
     */
    @Override
    List<? extends AnnotationMirror> getAnnotationMirrors();

    /**
     * 返回此元素针对指定类型的注解(如果存在这样的注解),否则返回 null。注解可以是继承的,也可以是直接存在于此元素上的
     */
    @Override
    <A extends Annotation> A getAnnotation(Class<A> annotationType);

}

Element常用子类

PackageElement 表示一个包程序元素
TypeElement 表示一个类或接口程序元素
VariableElement 表示一个字段、enum 常量、方法或构造方法参数、局部变量或异常参数
ExecutableElement 表示某个类或接口的方法、构造方法或初始化程序(静态或实例),包括注解类型元素
TypeParameterElement 表示一般类、接口、方法或构造方法元素的泛型参数
/**
 * 表示一个包程序元素.
 */
public interface PackageElement {
    
    

    /**
     * 返回此包的完全限定名称。该名称也是包的规范名称
     */
    Name getQualifiedName();

    /**
     * 如果此包是一个未命名的包,则返回 true,否则返回 false
     */
    boolean isUnnamed();
}
/**
 * 表示某个类或接口的方法、构造方法或初始化程序(静态或实例),包括注解类型元素
 */
public interface ExecutableElement {
    
    

    /**
     * 获取按照声明顺序返回形式类型参数元素
     */
    List<? extends TypeParameterElement> getTypeParameters();

    /**
     * 获取返回的类型元素
     */
    TypeMirror getReturnType();

    /**
     * 获取形参元素
     */
    List<? extends VariableElement> getParameters();


     /**
     * 如果此方法或构造方法接受可变数量的参数,则返回 true,否则返回 false
     */
    boolean isVarArgs();

    /**
     * 按声明顺序返回此方法或构造方法的 throws 子句中所列出的异常和其他 throwable
     */
    List<? extends TypeMirror> getThrownTypes();

    /**
     * 如果此 executable 是一个注解类型元素,则返回默认值。如果此方法不是注解类型元素,或者它是一个没有默认值的注解类型元素,则返回 null
     */
    AnnotationValue getDefaultValue();
}
/**
 * 表示一个字段、enum 常量、方法或构造方法参数、局部变量或异常参数
 */
public interface VariableElement {
    
    

    /**
     * 如果此变量是一个被初始化为编译时常量的 static final 字段,则返回此变量的值。否则返回 null。
     * 该值为基本类型或 String,如果该值为基本类型,则它被包装在适当的包装类中(比如 Integer)。
     * 注意,并非所有的 static final 字段都将具有常量值。特别是,enum 常量不 被认为是编译时常量。要获得一个常量值,字段的类型必须是基本类型或 String
     */
    Object getConstantValue();
}
/**
 * 表示一般类、接口、方法或构造方法元素的泛型参数
 */
public interface TypeParameterElement {
    
    

    /**
     * 返回由此类型参数参数化的一般类、接口、方法或构造方法
     */
    Element getGenericElement();

    /**
     * 返回此类型参数的边界。它们是用来声明此类型参数的 extends 子句所指定的类型。
     * 如果没有使用显式的 extends 子句,则认为 java.lang.Object 是唯一的边界
     */
    List<? extends TypeMirror> getBounds();
}

源码中的每个部分都作为Element,每个Element 的种类需要通过getKind() 方法返回的ElementKind枚举值来判断

public enum ElementKind {
    
    

    /** A package. */
    PACKAGE,
    /** An enum tye. */
    ENUM,
    /** A class not described by a more specific kind (like {@code ENUM}). */
    CLASS,
    /** An annotation type. */
    ANNOTATION_TYPE,
    /** An interface not described by a more specific kind */
    INTERFACE,

    // Variables
    /** An enum constant. */
    ENUM_CONSTANT,
    /** A field not described by a more specific kind */
    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,
    /**
     * A resource variable.
     * @since 1.7
     */
    RESOURCE_VARIABLE;
}

1.2 TypeMirror

ElementKind称作元素的种类,因为它和元素的类型TypeMirror 比较容易混淆。TypeMirror表示的是 Java 编程语言中的类型 , 比如上面例子中的字段String name,它的元素种类为FIELD,而它的元素类型为DECLARED表示一个类类型,这里对应Java 编程语言中的类型为java.lang.StringElement代表的是源代码上的元素,TypeMirror代表的是Element对应Java 编程语言中的类型。

/**
 * 表示 Java 编程语言中的类型
 */
public interface TypeMirror {
    
    
    /**
     * 返回此类型的种类,一个 TypeKind 枚举值:
     */
    TypeKind getKind();
}

TypeKind 表示此元素在java中的类型

public enum TypeKind {
    
    
    /** The primitive type {@codeoolean}. */
    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}. */
    VOID,
    /** A pseudo-type used where no actual type is appropriate. */
    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. */
    PACKAGE,
    /** A method, constructor, or initializer. */
    EXECUTABLE,
    /** An implementation-reserved type. This is not the type you are looking for. */
    OTHER,
    /** A union type. */
    UNION,
    /** An intersection type. */
    INTERSECTION;
}

TypeMirror的子类型如下:

ArrayType 代表数组类型,可通过API获取元数据类型
DeclaredType 声明类型,即类或接口。可以通过asElement()和后面的Element进行转换 ,List<? extends TypeMirror> getTypeArguments() 该方法可以获取DeclaredType的泛型
ExecutableType 构造器、初始化块等可执行体的类型,类似反射中Method,包含几个关键API:List<? extends TypeVariable> getTypeVariables()获取声明中的泛型、List<? extends TypeMirror> getParameterTypes()获取入参类型、TypeMirror getReturnType()获取返回值类型、List<? extends TypeMirror> getThrownTypes()获取声明的异常类型
TypeVariable 可以通过getLowerBound()和getUpperBound()获取类型限定、
WildcardType
其他 用的不是很多,参考文档

DeclaredType 可以通过asElement()方法转换为TypeElement , getTypeArguments() 则可以获取相关的泛型参数,比如 Map<String,String> map , map 属于 VariableElement , 它的TypeMirror属于DeclaredType, 因此可以转换为(typeMirror as DeclaredType).asElement() as TypeElement , getTypeArguments() 获取Map的泛型

2. 注解

2.1 声明注解
//java
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface RetrofitService {
    
    
    String name() default "";
}
//kotlin
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.CLASS)
annotation class ServiceRepository(val altName: String = "")

2.2 元注解

定义:用于解释注解的注解

常用元注解:

//1.@Target 表示这个注解(作用于)可以放到什么位置上面
/**
 * ElementType.ANNOTATION_TYPE //能修饰注解
 * ElementType.CONSTRUCTOR //能修饰构造器
 * ElementType.FIELD //能修饰成员变量
 * ElementType.LOCAL_VARIABLE //能修饰局部变量
 * ElementType.METHOD //能修饰方法
 * ElementType.PACKAGE //能修饰包名
 * ElementType.PARAMETER //能修饰参数
 * ElementType.TYPE //能修饰类、接口或枚举类型
 * ElementType.TYPE_PARAMETER //能修饰泛型,如泛型方法、泛型类、泛型接口 (jdk1.8加入)
 * ElementType.TYPE_USE //能修饰类型 可用于任意类型除了 class (jdk1.8加入)
 *
 */

//2.@Retention 表示注解的的生命周期
/**
 * RetentionPolicy.SOURCE //表示注解只在源码中存在,编译成 class 之后,就没了
 * RetentionPolicy.CLASS //表示注解在 java 源文件编程成 .class 文件后,依然存在,但是运行起来后就没了
 * RetentionPolicy.RUNTIME //表示注解在运行起来后依然存在,程序可以通过反射获取这些信息
 */

/**
*3.@Inherited  @Inherited 表示该注解可被继承,即当一个子类继承一个父类,
*该父类添加的注解有被 @Inherited 修饰,那么子类就可以获取到该注解,否则获取不到
*/

/**
*
*4.@Documented 表示该注解在通过 javadoc 命令生成 Api 文档后,会出现该注解的注释说明
*/

/**
*
*5.@Repeatable 是 JDK 1.8 新增的元注解,它表示注解在同一个位置能出现多次
*/

3.自定义一个注解解析器

3.1 依赖配置
dependencies {
    
    

    implementation project(':apt_annotations') // 依赖注解project

    api project(':http_network')

    implementation "org.jetbrains.kotlin:kotlin-stdlib:1.6.0"

    //用于启动自定义的processor,implementation和kapt auto-service
    implementation 'com.google.auto.service:auto-service:1.0-rc2'
    kapt 'com.google.auto.service:auto-service:1.0-rc2'

//    implementation "com.google.auto.service:auto-service:1.0"
//    kapt "com.google.auto.service:auto-service:1.0"
    //kotlinpoet,看需求也可以使用javapoet
    implementation "com.squareup:kotlinpoet:1.12.0"  
}

3.2 常用方法
@AutoService(Processor::class) //启动ServiceRepositoryProcessor
class ServiceRepositoryProcessor : AbstractProcessor(){
    
    

   private val projectNameKey = "projectName"

   //初始化方法,可以在该方法获取三个工具类对象实例 
   override fun init(processingEnv: ProcessingEnvironment?) {
    
    
        super.init(processingEnv)
        elementUtil = processingEnv!!.elementUtils //element处理工具
        filerUtil = processingEnv.filer //代码文件生成工具
        messager = processingEnv.messager //消息打印工具
    }

    //该自定义注解处理的注解类型,系统编译期间会对应注解丢给该自定义注解器处理
    override fun getSupportedAnnotationTypes(): MutableSet<String> {
    
    
        return mutableSetOf<String>().apply {
    
    
            add(ServiceRepository::class.java.canonicalName)
        }
    }

     //可配置的参数 
    /**
     * kapt {
     *  arguments {
     *  arg("projectName", "xxxx")
     *    }
     *  }
    */
    //在process()方法中 String xxxxValue = processingEnv.getOptions().get(projectNameKey);
     override fun getSupportedOptions(): MutableSet<String> {
    
    
        return mutableSetOf<String>().apply {
    
    
            add(projectNameKey)
        }
    }

    //支持的jdk版本
    override fun getSupportedSourceVersion(): SourceVersion {
    
    
        return SourceVersion.latestSupported()
    }


     // return true 表示getSupportedAnnotationTypes中的注解在那个处理器内处理完成
     // return false 表示会继续向下一个processor中传递getSupportedAnnotationTypes中注解数据
     override fun process(
        typeElements: MutableSet<out TypeElement>?,
        roundEnvironment: RoundEnvironment?
    ): Boolean{
    
    

     }

最后

如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。

扫描二维码关注公众号,回复: 16297578 查看本文章

如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
img
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。

欢迎大家一键三连支持,若需要文中资料,直接扫描文末CSDN官方认证微信卡片免费领取↓↓↓

PS:群里还设有ChatGPT机器人,可以解答大家在工作上或者是技术上的问题
图片

猜你喜欢

转载自blog.csdn.net/YoungOne2333/article/details/132027651