Android APT使用

APT是什么?有什么用?

APT(Annotation Processing Tool)即注解处理器,在编译的时候可以处理注解然后搞一些事情,也可以在编译时生成一些文件之类的
APT编译的时候 处理注解
APT传统方式 生成java文件
APT JavaPoet方式 生成java文件
ButterKnife和EventBus都使用了APT技术

image.png

image.png

传统方式:可读性强
javaPoet:看起来费劲

APT使用

1.创建arouter-annotation的java工程,这个工程用来写注解


image.png

2.创建ARouter注解

@Target({ElementType.TYPE})  //作用域在类上面
@Retention(RetentionPolicy.CLASS)  //编译期
public @interface ARouter {
    String path();

    String group() default  "" ;
}

3.创建compiler的java工程,这个工程用来写注解处理器


image.png

4.在build.gradle增加JavaPoet和autoService

dependencies{
    implementation fileTree(dir: 'libs', include: ['*.jar'])
      //路由
    compileOnly 'com.google.auto.service:auto-service:1.0-rc4'
    annotationProcessor 'com.google.auto.service:auto-service:1.0-rc4'
//如果是koltin用kapt注解
    //kapt 'com.google.auto.service:auto-service:1.0-rc4'

    implementation project(":arouter-annotation")
    //帮助我们通过类调用的形式来生成Java代码[JavaPoet]
    implementation "com.squareup:javapoet:1.10.0"
}

5.在compiler工程里面创建AbstractProcessor

@AutoService(Processor.class) //启用服务
@SupportedAnnotationTypes({"com.im.arouter_annotation.ARouter"}) 
@SupportedSourceVersion(SourceVersion.RELEASE_7)
public class  ARouterProcessor extends AbstractProcessor {
    //在编译期干活
    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {

        return false;
    }
}

6.在app的build.gradle里面关联arouter-annotation

    dependencies {
    implementation project(":arouter-annotation")
    annotationProcessor project(":compiler")
  //如果koltin 使用
 // apt project(":compiler")

}

7.在ARouterProcessor里面添加Elements,Types ,Messager ,Filer属性

@AutoService(Processor.class) //启用服务
@SupportedAnnotationTypes({"com.im.arouter_annotation.ARouter"})
@SupportedSourceVersion(SourceVersion.RELEASE_7)
public class  ARouterProcessor extends AbstractProcessor {
    //操作Element的工具类(类,函数,属性,其实都是Element )
    private Elements elements;

    //type( 类信息)的工具类,包含用于操作TypeMirror的工具方法
    private Types types;

    //用来打印日志相关信息
    private Messager messager;

    //文件生成器, 类资源等,就是最终要生成的文件是需要Filer来完成的
    private Filer filer;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        elements=processingEnv.getElementUtils();
        types=processingEnv.getTypeUtils();
        messager=processingEnv.getMessager();
        filer=processingEnv.getFiler();
    }

    //在编译期干活
    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        return false;
    }
}

8.在app的build.gradle添加配置

defaultConfig {
        //传参数
        javaCompileOptions {
            annotationProcessorOptions {
                arguments = ["TestName": "这是测试结果"]
            }
        }
        kapt {
            arguments {
                arg("TestName", "这是测试结果")
            }
        }
    }

9.在ARouterProcessor的init方法里面通过TestName拿到value

@AutoService(Processor.class) //启用服务
@SupportedAnnotationTypes({"com.im.arouter_annotation.ARouter"})
@SupportedSourceVersion(SourceVersion.RELEASE_7)
//接收参数
@SupportedOptions("TestName")
public class  ARouterProcessor extends AbstractProcessor {
    //操作Element的工具类(类,函数,属性,其实都是Element )
    private Elements elements;

    //type( 类信息)的工具类,包含用于操作TypeMirror的工具方法
    private Types types;

    //用来打印日志相关信息
    private Messager messager;

    //文件生成器, 类资源等,就是最终要生成的文件是需要Filer来完成的
    private Filer filer;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);

        elements=processingEnv.getElementUtils();
        types=processingEnv.getTypeUtils();
        messager=processingEnv.getMessager();
        filer=processingEnv.getFiler();

        String value=processingEnv.getOptions().get("TestName");
        //需要注意的是不能使用Diagnostic.Kind.ERROR  ,如果想让注解处理器抛出异常就使用Diagnostic.Kind.ERROR
        messager.printMessage(Diagnostic.Kind.NOTE,"测试一下===="+value);
    }

  
    //在编译期干活
    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        messager.printMessage(Diagnostic.Kind.NOTE,"执行process");
        return false;
    }
}

9.使用Make Project


image.png

image.png

只有在用到ARouter注解的时候ARouterProcessor才会执行里面的方法

9.通过APT创建Test类

@AutoService(Processor.class) //启用服务
@SupportedAnnotationTypes({"com.im.arouter_annotation.ARouter"})
@SupportedSourceVersion(SourceVersion.RELEASE_7)
//接收参数
@SupportedOptions("TestName")
public class  ARouterProcessor extends AbstractProcessor {
    //操作Element的工具类(类,函数,属性,其实都是Element )
    private Elements elements;

    //type( 类信息)的工具类,包含用于操作TypeMirror的工具方法
    private Types types;

    //用来打印日志相关信息
    private Messager messager;

    //文件生成器, 类资源等,就是最终要生成的文件是需要Filer来完成的
    private Filer filer;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);

        elements=processingEnv.getElementUtils();
        types=processingEnv.getTypeUtils();
        messager=processingEnv.getMessager();
        filer=processingEnv.getFiler();

        String value=processingEnv.getOptions().get("TestName");
        //需要注意的是不能使用Diagnostic.Kind.ERROR  ,如果想让注解处理器抛出异常就使用Diagnostic.Kind.ERROR
        messager.printMessage(Diagnostic.Kind.NOTE,"测试一下===="+value);
    }

    //在编译期干活
    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        messager.printMessage(Diagnostic.Kind.NOTE,"执行process");
        /*
            package com.example.helloworld;

            public final class HelloWorld {
            public static void main(String[] args) {
            System.out.println("Hello, JavaPoet!");
            }
            }
        * */
        //拿到注解
        Set<? extends Element> ARouters = roundEnvironment.getElementsAnnotatedWith(ARouter.class);
        for (Element aRouter : ARouters) {
            //先写方法  类 和包

            //生成一个public static void main(String[] args)的方法
            //"$T.out.println($S)" $T相当于一个占位符 和System.class 对应
            //"$S相当于一个占位符 和Hello, JavaPoet!对应
            MethodSpec methodSpec=MethodSpec.methodBuilder("main").addModifiers(Modifier.PUBLIC
            ,Modifier.STATIC).returns(void.class).addParameter(String[].class,"args").
                    addStatement("$T.out.println($S)",System.class,"Hello, JavaPoet!").
                    build();
            //创建一个 public final class Test 类
            // 并且把methodSpec方法传进去
            TypeSpec typeSpec = TypeSpec.classBuilder("Test").
                    addMethod(methodSpec).addModifiers(Modifier.PUBLIC,Modifier.FINAL).
                    build();

            JavaFile javaFile = JavaFile.builder("com.compiler.test", typeSpec).build();

  //也可以指定其他参数类型
//           try {
//                Class<?> aClass = Class.forName("android.app.Activity");
//                MethodSpec methodSpec2=MethodSpec.methodBuilder("start").addModifiers(Modifier.PUBLIC
//                        ).returns(void.class).addParameter(aClass,"args").
//                        addStatement("$T.out.println($S)",System.class,"Hello, JavaPoet!").
//                        build();
//                TypeSpec typeSpec2 = TypeSpec.classBuilder("Test2").
//                        addMethod(methodSpec2).addModifiers(Modifier.PUBLIC,Modifier.FINAL).
//                        build();
//                JavaFile javaFile2 = JavaFile.builder("com.compiler.test", typeSpec2).build();
//                javaFile2.writeTo(filer);
//
//            } catch (ClassNotFoundException | IOException e) {
//                e.printStackTrace();
//            }



            //生成文件
            try {
                javaFile.writeTo(filer);
            } catch (IOException e) {
                e.printStackTrace();
                messager.printMessage(Diagnostic.Kind.NOTE,"javaFile:"+e.getMessage());

            }
        }
        return false;  //一般不用管这里的返回值
    }
}
image.png

image.png

原理:
原理: 编写好的 Java 源文件,需要经过 javac 的编译,翻译为虚拟机能够加载解析的字节码 Class 文件。注解处理器是 javac 自带的一个工具,用来在编译时期扫描处理注解信息。你可以为某些注解注册自己的注解处理器。 注册的注解处理器由 javac调起,并将注解信息传递给注解处理器进行处理


image.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容