-
APT是什么?有什么用?
APT(Annotation Processing Tool)即注解处理器,在编译的时候可以处理注解然后搞一些事情,也可以在编译时生成一些文件之类的。ButterKnife和EventBus都使用了APT技术,如果不会APT技术就很难看懂这两个框架的源码。

-
实现效果
我们来实现一个简单的功能,只要在任何类的成员变量上添加一个 @Print注解,就可以动态生成一个方法,然后把成员变量的变量名输出:

动态生成的类大概长这样:

-
整理思路
- 首先我们需要创建两个JavaLibrary
- 一个用来定义注解,一个用来扫描注解
- 获取到添加注解的成员变量名
- 动态生成类和方法用IO生成文件
-
实战
-
创建一个空项目

-
创建两个JavaLibrary
- 注解的Lib:
apt-annotation - 扫描注解的Lib:
apt-processor


-
创建完之后

-
app模块依赖两个Library
implementation project(path: ':apt-annotation')
annotationProcessor project(path: ':apt-processor')
复制代码

-
注解Lib中创建一个注解类
如果还不会自定义注解的同学,可以先去看我之前写的一篇Java自定义注解入门到实战
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.FIELD)
public @interface Print {
}
复制代码

-
扫描注解的Lib添加依赖
dependencies {
//自动注册,动态生成 META-INF/...文件
implementation 'com.google.auto.service:auto-service:1.0-rc6'
annotationProcessor 'com.google.auto.service:auto-service:1.0-rc6'
//依赖apt-annotation
implementation project(path: ':apt-annotation')
}
复制代码

-
创建扫描注解的类

-
重写init方法,输出Hello,APT
注意: 这里是JavaLib,所以不能使用Log打印,这里可以使用Java的println()或注解处理器给我们提供的方法,建议使用注解处理器给我们提供的

-
见证奇迹
现在我们已经完成了APT的基本配置,现在我们可以build一下项目了,成败在此一举

-
踩坑指南
- 如果你已经成功输出了文本,说明APT已经配置好,可以继续下一步了
如果你失败了:
- 如果继承的时候找不到AbstractProcessor类,那你
创建的肯定不是JavaLibrary,你可以删掉重新创建- 如果点击编译没反应,你可以试试先
clear一下项目再重新编译- 如果都不行,就去检查一下前面流程的
依赖是否都配置正确
-
继续完成功能
现在我们可以继续完成上面要实现的功能了,我们需要先来实现几个方法
/**
* 要扫描扫描的注解,可以添加多个
*/
@Override
public Set<String> getSupportedAnnotationTypes() {
HashSet<String> hashSet = new HashSet<>();
hashSet.add(Print.class.getCanonicalName());
return hashSet;
}
/**
* 编译版本,固定写法就可以
*/
@Override
public SourceVersion getSupportedSourceVersion() {
return processingEnv.getSourceVersion();
}
复制代码

-
定义注解
我们先在MianActivity中添加两个成员变量并使用我们定义的注解

-
定义注解
真正解析注解的地方是在process方法,我们先试试能不能拿到被注解的变量名
/**
* 扫描注解回调
*/
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
//拿到所有添加Print注解的成员变量
Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(Print.class);
for (Element element : elements) {
//拿到成员变量名
Name simpleName = element.getSimpleName();
//输出成员变量名
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE,simpleName);
}
return false;
}
复制代码

-
编译试一下

-
生成类
既然能拿到被注解的变量名,后面就简单了,我们只需要用字符串拼出来一个工具类,然后用IO流写到本地就ok了

-
查看效果
现在点击一下编译,然后我们可以看到app模块下的build文件已经有我们生成的类了

-
调用方法
现在我们回到MainActivity,就可以直接调用这个动态生成的类了


-
实战结束
结束了吗…好像是结束了,但是上面拼接类的方法感觉一不小心就会写错,有没有更好的方法呢,我们先来看看EventBus的源码是怎么生成的:

看到大佬也是这样拼接的,这我就放心了?,我们再看一下ButterKnife的源码是怎么生成的:

ButterKnife的源码竟然不是用字符串拼接的!!! 隐约看到TypeSpec.classBuilder,这是啥玩意?不过身为资深的程序猿这点问题我们还是可以很容易的找到答案的

-
JavaPoet
经过一个小时的百度,大概研究了一下JavaPoet,这玩意好像可以帮我们以面向对象的思维来生成类,这样我们就不用手动拼接字符串的方式来生成类了,那我们来优化一下上面的代码:
先添加依赖
implementation 'com.squareup:javapoet:1.13.0'
复制代码

/**
* 扫描注解回调
*/
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
//拿到所有添加Print注解的成员变量
Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(Print.class);
//生成类
TypeSpec.Builder classBuilder = TypeSpec
.classBuilder("PrintUtil")
.addModifiers(Modifier.PUBLIC, Modifier.FINAL);
for (Element element : elements) {
//拿到成员变量名
Name simpleName = element.getSimpleName();
//生成方法
MethodSpec method = MethodSpec.methodBuilder("print$$"+simpleName)
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.returns(void.class)
.addStatement("$T.out.println($S)", System.class, "Hello, JavaPoet!")
.build();
classBuilder.addMethod(method);
}
//包
JavaFile javaFile = JavaFile
.builder("com.lkx.helloapt", classBuilder.build())
.build();
try {
javaFile.writeTo(processingEnv.getFiler());
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
复制代码

-
编译一下


-
总结
- APT可以在编译器扫描注解帮我们提前生成类
- JavaPoet可以帮我们优雅的生成类,再也不用拼接了
- APT最主要的功能就是可以替代反射的一些功能,避免降低性能
- APT只会在编译时影响一点点速度,在运行期不会,而反射刚好相反



















![[02/27][官改] Simplicity@MIX2 ROM更新-一一网](https://www.proyy.com/wp-content/uploads/2020/02/3168457341.jpg)



![[桜井宁宁]COS和泉纱雾超可爱写真福利集-一一网](https://www.proyy.com/skycj/data/images/2020-12-13/4d3cf227a85d7e79f5d6b4efb6bde3e8.jpg)

![[桜井宁宁] 爆乳奶牛少女cos写真-一一网](https://www.proyy.com/skycj/data/images/2020-12-13/d40483e126fcf567894e89c65eaca655.jpg)