美文网首页注解
编译时注解学习三之 注解处理器AbstractProcessor

编译时注解学习三之 注解处理器AbstractProcessor

作者: sliencexiu | 来源:发表于2019-08-01 14:26 被阅读2次

    1 如何读取build.gradle配置的常量

    配置,读取:

    @SupportedOptions({"CLASSNAME"})
    @SupportedAnnotationTypes("com.ldx.annotationlib.BindView")
    @SupportedSourceVersion(SourceVersion.RELEASE_7)
    //@AutoService(Processor.class)
    public class AptProcessor extends AbstractProcessor {
    
        private Map<String,String> mOptionMap;
    
        @Override
        public synchronized void init(ProcessingEnvironment processingEnvironment) {
            super.init(processingEnvironment);
            mOptionMap = processingEnv.getOptions();
        }
    
        @Override
        public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
            if (set != null && set.size() > 0){
    
                Set<? extends Element>  elements = roundEnvironment.getElementsAnnotatedWith(BindView.class);
                for (Element element : elements) {
                    VariableElement variableElement = (VariableElement) element;
                    //读取配置参数
                    mMessager.printMessage(Diagnostic.Kind.NOTE,"aptprocessor     "+mOptionMap.get("CLASSNAME"));
    
                    createFile(enclosingElement, bindViewFiledClassType, bindViewFiledName, bindAnnotation.value());
                }
    
                return true;
            }
            return false;
        }
    

    build.gradle中配置(如何配置编译时注解变量):

     javaCompileOptions {
                annotationProcessorOptions {
                    arguments = ["xxxx":"xxxxx", "CLASSNAME":"lidxclassname"]
    
                }
            }
    

    2 打印信息

    由于在编译期,所以无法利用System,log,打印信息,系统提供了Messager工具进行信息的打印。
    private Messager mMessager = processingEnv.getMessager();;


    打印日志的几个方法

    第一个参数Kind标识日志的级别,包括:

     public static enum Kind {
            ERROR,
            WARNING,
            MANDATORY_WARNING,
            NOTE,
            OTHER;
    
            private Kind() {
            }
        }
    

    第二个参数为打印信息,后面参数Element为需要打印的Element。

    3 Elements 工具

    Elements mElementsUtils = processingEnv.getElementUtils();


    方法
      //包名
    mMessager.printMessage(Diagnostic.Kind.NOTE,"aptprocessor     "+mElementsUtils.getPackageOf(enclosingElement).asType().toString());
    mMessager.printMessage(Diagnostic.Kind.NOTE,"aptprocessortwo     "+mElementsUtils.getBinaryName(enclosingElement));
    mMessager.printMessage(Diagnostic.Kind.NOTE,"aptprocessortwo     "+mElementsUtils.getPackageOf(enclosingElement).getQualifiedName().toString());
    mMessager.printMessage(Diagnostic.Kind.NOTE,"aptprocessortwo     "+mElementsUtils.isDeprecated(enclosingElement));
    mMessager.printMessage(Diagnostic.Kind.NOTE,"aptprocessortwo     "+mElementsUtils.getPackageElement("com.ldx.canvasdrawdemo").getQualifiedName());
    
    

    4 Filer,生成文件的工具类

    Filer mFilerUtils = processingEnv.getFiler();
    完全类名:javax.annotation.processing.Filer,注解处理器可用此创建新文件(源文件、类文件、辅助资源文件)。由此方法创建的源文件和类文件将由管理它们的工具(javac)处理


    介绍 生成文件方法

    apt常用方法为:


    参数解释
    • 第一个参数为生成的java文件的全限定名(也就是包名和文件名,写法应该是包名(com.ldx.xx)然后加上一个点(.)在加上文件名),例如要生成com.ldx.xx.demo.java,第一个参数写法为"com.ldx.xx.demo",其中demo就是文件名。
    • 第二个参数是与此文件的创建有因果关联的类型或包元素,该参数可以省略或者为 null,类型为一个数组,一般写法为new Element[]{}或者为null。
    //例子
      private void createFile(TypeElement enclosingElement, String bindViewFiledClassType, String bindViewFiledName, int id) {
            String pkName = mElementsUtils.getPackageOf(enclosingElement).getQualifiedName().toString();
            String packName = mElementsUtils.getPackageOf(enclosingElement).asType().toString();
            String className = enclosingElement.getSimpleName().toString();
            try {
                JavaFileObject jfo = mFilerUtils.createSourceFile(pkName + "."+className+ "$ViewBinding", new Element[]{});
                Writer writer = jfo.openWriter();
                writer.write(brewCode(className,pkName, bindViewFiledClassType, bindViewFiledName, id));
                writer.flush();
                writer.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        private String brewCode(String className,String pkName, String bindViewFiledClassType, String bindViewFiledName, int id) {
            StringBuilder builder = new StringBuilder();
            builder.append("package " + pkName + ";\n\n");
            builder.append("public class " + className + "$ViewBinding { \n\n");
            builder.append("public static void main(String[] args){ \n\n");
            String info = String.format("%s %s = findViewById(%d)", bindViewFiledClassType, bindViewFiledName, id);
            builder.append("System.out.println(\"" + info + "\");\n\n");
            builder.append("}\n\n");
            builder.append("}");
            return builder.toString();
        }
    

    5 ElementKind

    如何判断Element的类型呢,需要用到ElementKind,ElementKind为元素的类型,元素的类型判断不需要用instanceof去判断,而应该通过getKind()去判断对应的类型
    类型 说明
    PACKAGE 包
    ENUM 枚举
    CLASS 类
    ANNOTATION_TYPE 注解
    INTERFACE 接口
    ENUM_CONSTANT 枚举常量
    FIELD 字段
    PARAMETER 方法参数
    LOCAL_VARIABLE 局部变量
    METHOD 方法
    CONSTRUCTOR 构造方法
    TYPE_PARAMETER 类型参数

      Set<? extends Element>  elements = roundEnvironment.getElementsAnnotatedWith(BindView.class);
                for (Element element : elements) {
                    ElementKind kind = element.getKind();
                    if (kind.isField()){
                       mMessager.printMessage(Diagnostic.Kind.NOTE,"aptprocessor     "+"element 是 Field,利用VariableElement进行强转");
                    }
                    }
    
    

    6 TypeMirror

    TypeMirror是一个接口,表示Java编程语言中的类型。这些类型包括基本类型、引用类型、数组类型、类型变量和null类型等等

    类型 说明
    ArrayType 表示数组类型
    DeclaredType 表示声明类型(类或接口类型)
    ErrorType 表示异常类或接口类型
    ExecutableType 表示executable类型(方法、构造方法、初始化)
    NoType 表示在实际类型不适合的地方使用的伪类型
    NullType 表示null类型
    PrimitiveType 表示基本数据类型
    ReferenceType 表示引用类型
    TypeVariable 表示类型变量
    WildcardType 表示通配符类型参数

     for (Element element : elements) {
                    ElementKind kind = element.getKind();
                    if (kind.isField()){
                        mMessager.printMessage(Diagnostic.Kind.NOTE,"aptprocessor     "+"element 是 Field,利用VariableElement进行强转");
                        TypeMirror mirror = element.asType();
                        
                        TypeKind kind1 = mirror.getKind();
                        
                    }
    

    如果Element是一个VariableElement,variableElement.asType().toString()可以获取它的全类名。

      String bindViewFiledClassType = variableElement.asType().toString();
                    //变量名
                    mMessager.printMessage(Diagnostic.Kind.NOTE,"aptprocessor     "+bindViewFiledClassType);
    

    输出:
    android.widget.TextView

    7 TypeKind

    TypeKind为类型的属性,类型的属性判断不需要用instanceof去判断,而应该通过getKind()去判断对应的属性

    类型 说明
    BOOLEAN 基本类型boolean
    INT 基本类型int
    LONG 基本类型long
    FLOAT 基本类型float
    DOUBLE 基本类型double
    VOID 对应于关键字void的伪类型
    NULL null类型
    ARRAY 数组类型
    PACKAGE 对应于包元素的伪类型
    EXECUTABLE 方法、构造方法、初始化
    DECLARE 声明类型

     for (Element element : elements) {
                    ElementKind kind = element.getKind();
                    if (kind.isField()){
                        mMessager.printMessage(Diagnostic.Kind.NOTE,"aptprocessor     "+"element 是 Field,利用VariableElement进行强转");
                        TypeMirror mirror = element.asType();
                        
                        TypeKind kind1 = mirror.getKind();
                        
                    }
    

    8 Types

    Types mTypesUtils = processingEnv.getTypeUtils();
    Type操作工具,包括对TypeMirror,TypeKind,DeclaredType


    方法1 方法2

    asElement,根据特定TypeMirror获取与之关联的ElementType。
    contains,是否一个TypeMirror包含另外一个TypeMirror,类似数组和普通元素。
    。。。。。。。

    相关文章

      网友评论

        本文标题:编译时注解学习三之 注解处理器AbstractProcessor

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