APT是什么?有什么用?
APT(Annotation Processing Tool)即注解處理器,在編譯的時候可以處理注解然后搞一些事情,也可以在編譯時生成一些文件之類的
APT編譯的時候 處理注解
APT傳統(tǒng)方式 生成java文件
APT JavaPoet方式 生成java文件
ButterKnife和EventBus都使用了APT技術(shù)


傳統(tǒng)方式:可讀性強
javaPoet:看起來費勁
APT使用
1.創(chuàng)建arouter-annotation的java工程,這個工程用來寫注解

2.創(chuàng)建ARouter注解
@Target({ElementType.TYPE}) //作用域在類上面
@Retention(RetentionPolicy.CLASS) //編譯期
public @interface ARouter {
String path();
String group() default "" ;
}
3.創(chuàng)建compiler的java工程,這個工程用來寫注解處理器

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")
//幫助我們通過類調(diào)用的形式來生成Java代碼[JavaPoet]
implementation "com.squareup:javapoet:1.10.0"
}
5.在compiler工程里面創(chuàng)建AbstractProcessor
@AutoService(Processor.class) //啟用服務(wù)
@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里面關(guān)聯(lián)arouter-annotation
dependencies {
implementation project(":arouter-annotation")
annotationProcessor project(":compiler")
//如果koltin 使用
// apt project(":compiler")
}
7.在ARouterProcessor里面添加Elements,Types ,Messager ,Filer屬性
@AutoService(Processor.class) //啟用服務(wù)
@SupportedAnnotationTypes({"com.im.arouter_annotation.ARouter"})
@SupportedSourceVersion(SourceVersion.RELEASE_7)
public class ARouterProcessor extends AbstractProcessor {
//操作Element的工具類(類,函數(shù),屬性,其實都是Element )
private Elements elements;
//type( 類信息)的工具類,包含用于操作TypeMirror的工具方法
private Types types;
//用來打印日志相關(guān)信息
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 {
//傳參數(shù)
javaCompileOptions {
annotationProcessorOptions {
arguments = ["TestName": "這是測試結(jié)果"]
}
}
kapt {
arguments {
arg("TestName", "這是測試結(jié)果")
}
}
}
9.在ARouterProcessor的init方法里面通過TestName拿到value
@AutoService(Processor.class) //啟用服務(wù)
@SupportedAnnotationTypes({"com.im.arouter_annotation.ARouter"})
@SupportedSourceVersion(SourceVersion.RELEASE_7)
//接收參數(shù)
@SupportedOptions("TestName")
public class ARouterProcessor extends AbstractProcessor {
//操作Element的工具類(類,函數(shù),屬性,其實都是Element )
private Elements elements;
//type( 類信息)的工具類,包含用于操作TypeMirror的工具方法
private Types types;
//用來打印日志相關(guān)信息
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,"執(zhí)行process");
return false;
}
}
9.使用Make Project


只有在用到ARouter注解的時候ARouterProcessor才會執(zhí)行里面的方法
9.通過APT創(chuàng)建Test類
@AutoService(Processor.class) //啟用服務(wù)
@SupportedAnnotationTypes({"com.im.arouter_annotation.ARouter"})
@SupportedSourceVersion(SourceVersion.RELEASE_7)
//接收參數(shù)
@SupportedOptions("TestName")
public class ARouterProcessor extends AbstractProcessor {
//操作Element的工具類(類,函數(shù),屬性,其實都是Element )
private Elements elements;
//type( 類信息)的工具類,包含用于操作TypeMirror的工具方法
private Types types;
//用來打印日志相關(guān)信息
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,"執(zhí)行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相當(dāng)于一個占位符 和System.class 對應(yīng)
//"$S相當(dāng)于一個占位符 和Hello, JavaPoet!對應(yīng)
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();
//創(chuàng)建一個 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();
//也可以指定其他參數(shù)類型
// 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; //一般不用管這里的返回值
}
}


原理:
原理: 編寫好的 Java 源文件,需要經(jīng)過 javac 的編譯,翻譯為虛擬機能夠加載解析的字節(jié)碼 Class 文件。注解處理器是 javac 自帶的一個工具,用來在編譯時期掃描處理注解信息。你可以為某些注解注冊自己的注解處理器。 注冊的注解處理器由 javac調(diào)起,并將注解信息傳遞給注解處理器進行處理
