【Android APT】注解处理器 ( Element 注解节点相关操作 )
文章目录
- 一、获取被 注解 标注的节点
- 二、Element 注解节点类型
- 三、VariableElement 注解节点相关操作
- 四、注解处理器 完整代码示例
- 五、博客资源
Android APT 学习进阶路径 : 推荐按照顺序阅读 , 从零基础到开发简易 ButterKnife 注解框架的学习路径 ;
- 【Java 注解】注解简介及作用
- 【Java 注解】自定义注解 ( 注解属性定义与赋值 )
- 【Java 注解】自定义注解 ( 元注解 )
- 【Java 注解】自定义注解 ( 注解解析 )
- 【Java 注解】自定义注解 ( 使用注解实现简单测试框架 )
- 【Android APT】编译时技术 ( ButterKnife 原理分析 )
- 【Android APT】编译时技术 ( 编译时注解 和 注解处理器 依赖库 )
- 【Android APT】编译时技术 ( 开发编译时注解 )
- 【Android APT】注解处理器 ( 注解标注 与 初始化方法 )
- 【Android APT】注解处理器 ( 配置注解依赖、支持的注解类型、Java 版本支持 )
- 【Android APT】注解处理器 ( Element 注解节点相关操作 )
上一篇博客 【Android APT】注解处理器 ( 配置注解依赖、支持的注解类型、Java 版本支持 ) 中 为 注解处理器 Module 添加了 编译时注解 Module 依赖 , 并设置了支持该注解处理器 支持的 注解类型 , 和 支持的 Java 版本 ;
本篇博客开发 注解处理器 的 处理注解 , 生成代码的核心逻辑 ;
一、获取被 注解 标注的节点
处理注解的核心逻辑在 AbstractProcessor 中的 process 方法中实现 ;
@AutoService(Processor.class)
public class Compiler extends AbstractProcessor {/*** 搜索 Android 代码中的 BindView 注解* 并生成相关代码* @param annotations* @param roundEnv* @return*/@Overridepublic boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {return false;}
}
先获取被注解标注的节点 , 搜索 BindView , 调用 process
方法的 RoundEnvironment roundEnv
参数的 getElementsAnnotatedWith
方法 , 即可搜索到整个 Module 中所有使用了 BindView 注解的元素 ;
// 搜索 BindView , 将 BindView 注解放在什么元素上 , 得到的就是相应类型的元素
// 根据 注解类型 获取 被该注解类型 标注的元素 , 元素可能是类 , 方法 , 字段 ;
// 通过 getElementsAnnotatedWith 方法可以搜索到整个 Module 中所有使用了 BindView 注解的元素
Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(BindView.class);
将 BindView 注解放在什么元素上 , 得到的就是相应类型的元素 , 根据 注解类型 获取 被该注解类型 标注的元素 , 元素可能是类 , 方法 , 字段 ;
在 app 模块中 , 只有 MainActivity
中的一个 属性字段 使用 BindView 注解 , 调用 roundEnv.getElementsAnnotatedWith(BindView.class)
方法获取的元素就是 MainActivity
中的 TextView hello
成员变量 ;
import android.os.Bundle;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import kim.hsl.annotation.BindView;public class MainActivity extends AppCompatActivity {@BindView(R.id.hello)TextView hello;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}
}
假设在 若干 Activity 中 , 若干位置 , 使用了 BindView 注解 , 那么在获取的所有使用了 BindView 注解的字段 Set<? extends Element> elements
中装载了所有的使用了该注解的字段 , 这些字段来自不同的 Activity 中 ;
这就需要将 Set<? extends Element> elements
中的 字段 按照 Activity 上下文进行分组 , 以便生成代码 ;
这样每个 Activity 界面都对应若干个 Set<? extends Element> elements
中的元素 ;
二、Element 注解节点类型
使用注解标注的 Element 节点类型 :
ExecutableElement : 使用注解的 方法 节点类型 ;
VariableElement : 使用注解的 字段 节点类型 , 类的成员变量 ;
TypeElement : 使用注解的 类 节点类型 ;
PackageElement : 使用注解的 包 节点类型 ;
上述 444 个类都是 javax.lang.model.element.Element 的子类 ;
@BindView 注解标注的都是 Activity 中的成员字段 , 当前获取 Set<? extends Element> elements
集合中的节点都是 Field 字段对应的 VariableElement 类型的节点 ;
三、VariableElement 注解节点相关操作
遍历上述 VariableElement 类型节点集合 , 将其中的元素按照 Activity 进行分组 , 将分组结果放到 HashMap<String, HashSet<VariableElement>> elementMap
键值对 Map 集合中 ;
要分组放置 注解节点 的 HashMap<String, HashSet<VariableElement>> elementMap
键值对 Map 集合 , 其中 " 键 " 是 注解标注的成员字段所在的 Activity 类的全类名 , " 值 " 是该 Activity 中所有使用 @BindView 注解的成员字段集合 ;
// @BindView 注解标注的都是 Activity 中的成员字段,
// 上述 elements 中的元素都是 VariableElement 类型的节点
HashMap<String, HashSet<VariableElement>> elementMap = new HashMap<>();
给定 VariableElement
注解节点 , 获取该节点的上一级节点 , 注解节点是 VariableElement
, 成员字段节点 , 其上一级节点是就是 Activity
类对应的 类节点 TypeElement
, 通过调用 VariableElement.getEnclosingElement
方法获取上一级节点 , 类型是 TypeElement
;
// 将注解节点类型强转为 VariableElement 类型
VariableElement ve = (VariableElement) element;// 获取该注解节点对应的成员变量类名
// 先获取该注解节点的上一级节点 , 注解节点是 VariableElement , 成员字段节点
// 上一级节点是就是 Activity 类节点对应的 类节点 TypeElement
TypeElement te = (TypeElement) ve.getEnclosingElement();
获取 TypeElement
注解节点 的全类名 , 调用 TypeElement.getQualifiedName
方法获取 , 如果只获取类名 , 不包含完整包名的话 , 调用 TypeElement.getSimpleName
方法获取 ;
// 获取 Activity 的全类名
String className = te.getQualifiedName().toString();
根据从 VariableElement 对象中获取的上一级节点的类名 , 对 Set<? extends Element> elements
集合中的 VariableElement 类型元素进行分组 , 同一个 Activity 中对应的 注解节点 对象放在一个 HashSet<VariableElement>
集合中 , 然后放到 HashMap<String, HashSet<VariableElement>> elementMap
中 , 键是 Activity 全类名 ;
具体的集合操作参考下面的源码示例 ;
部分代码示例 :
/*** 搜索 Android 代码中的 BindView 注解* 并生成相关代码* @param annotations* @param roundEnv* @return*/@Overridepublic boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {// 搜索 BindView , 将 BindView 注解放在什么元素上 , 得到的就是相应类型的元素// 根据 注解类型 获取 被该注解类型 标注的元素 , 元素可能是类 , 方法 , 字段 ;// 通过 getElementsAnnotatedWith 方法可以搜索到整个 Module 中所有使用了 BindView 注解的元素Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(BindView.class);// @BindView 注解标注的都是 Activity 中的成员字段,// 上述 elements 中的元素都是 VariableElement 类型的节点HashMap<String, HashSet<VariableElement>> elementMap = new HashMap<>();// 遍历 elements 注解节点 , 为节点分组for (Element element : elements){// 将注解节点类型强转为 VariableElement 类型VariableElement ve = (VariableElement) element;// 获取该注解节点对应的成员变量类名// 先获取该注解节点的上一级节点 , 注解节点是 VariableElement , 成员字段节点// 上一级节点是就是 Activity 类节点对应的 类节点 TypeElementTypeElement te = (TypeElement) ve.getEnclosingElement();// 获取 Activity 的全类名String className = te.getQualifiedName().toString();mMessager.printMessage(Diagnostic.Kind.NOTE, "TypeElement : " + className + " , VariableElement : " + ve.getSimpleName());// 获取 elementMap 集合中的 Activity 的全类名对应的 VariableElement 节点集合// 如果是第一次获取 , 为空 ,// 如果之前已经获取了该 Activity 的全类名对应的 VariableElement 节点集合, 那么不为空HashSet<VariableElement> variableElements = elementMap.get(className);if (variableElements == null){variableElements = new HashSet<>();// 创建之后 , 将集合插入到 elementMap 集合中elementMap.put(className, variableElements);}// 将本节点插入到 HashSet<VariableElement> variableElements 集合中variableElements.add(ve);}return false;}
编译时 注解处理器 打印的内容 : 在 Build 面板 , " Build Output " 选项卡中输入编译相关信息 , 其中 " Task :app:compileDebugJavaWithJavac " 任务输出 注解处理器 相关日志 ;
四、注解处理器 完整代码示例
注解处理器 完整代码示例 :
package kim.hsl.annotation_compiler;import com.google.auto.service.AutoService;import java.io.File;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.tools.Diagnostic;import kim.hsl.annotation.BindView;/*** 生成代码的注解处理器*/
@AutoService(Processor.class)
public class Compiler extends AbstractProcessor {/*** 生成 Java 代码对象*/private Filer mFiler;/*** 日志打印*/private Messager mMessager;/*** 初始化注解处理器相关工作* @param processingEnv*/@Overridepublic synchronized void init(ProcessingEnvironment processingEnv) {super.init(processingEnv);this.mFiler = processingEnv.getFiler();this.mMessager = processingEnv.getMessager();}/*** 声明 注解处理器 要处理的注解类型* @return*/@Overridepublic Set<String> getSupportedAnnotationTypes() {Set<String> supportedAnnotationTypes = new HashSet<String>();// 将 BindView 全类名 kim.hsl.annotation.BinndView 放到 Set 集合中supportedAnnotationTypes.add(BindView.class.getCanonicalName());return supportedAnnotationTypes;}/*** 声明支持的 JDK 版本* @return*/@Overridepublic SourceVersion getSupportedSourceVersion() {// 通过 ProcessingEnvironment 类获取最新的 Java 版本并返回return processingEnv.getSourceVersion();}/*** 搜索 Android 代码中的 BindView 注解* 并生成相关代码* @param annotations* @param roundEnv* @return*/@Overridepublic boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {// 搜索 BindView , 将 BindView 注解放在什么元素上 , 得到的就是相应类型的元素// 根据 注解类型 获取 被该注解类型 标注的元素 , 元素可能是类 , 方法 , 字段 ;// 通过 getElementsAnnotatedWith 方法可以搜索到整个 Module 中所有使用了 BindView 注解的元素Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(BindView.class);// @BindView 注解标注的都是 Activity 中的成员字段,// 上述 elements 中的元素都是 VariableElement 类型的节点HashMap<String, HashSet<VariableElement>> elementMap = new HashMap<>();// 遍历 elements 注解节点 , 为节点分组for (Element element : elements){// 将注解节点类型强转为 VariableElement 类型VariableElement ve = (VariableElement) element;// 获取该注解节点对应的成员变量类名// 先获取该注解节点的上一级节点 , 注解节点是 VariableElement , 成员字段节点// 上一级节点是就是 Activity 类节点对应的 类节点 TypeElementTypeElement te = (TypeElement) ve.getEnclosingElement();// 获取 Activity 的全类名String className = te.getQualifiedName().toString();mMessager.printMessage(Diagnostic.Kind.NOTE, "TypeElement : " + className + " , VariableElement : " + ve.getSimpleName());// 获取 elementMap 集合中的 Activity 的全类名对应的 VariableElement 节点集合// 如果是第一次获取 , 为空 ,// 如果之前已经获取了该 Activity 的全类名对应的 VariableElement 节点集合, 那么不为空HashSet<VariableElement> variableElements = elementMap.get(className);if (variableElements == null){variableElements = new HashSet<>();// 创建之后 , 将集合插入到 elementMap 集合中elementMap.put(className, variableElements);}// 将本节点插入到 HashSet<VariableElement> variableElements 集合中variableElements.add(ve);}return false;}
}
五、博客资源
博客源码 :
GitHub : https://github.com/han1202012/APT
CSDN :
【Android APT】注解处理器 ( Element 注解节点相关操作 )相关推荐
- 【Android APT】注解处理器 ( 根据注解生成 Java 代码 )
文章目录 一.生成 Java 代码 二.实现 IButterKnife 接口 三.视图绑定主要操作 四.完整注解处理器代码 五.博客资源 Android APT 学习进阶路径 : 推荐按照顺序阅读 , ...
- 【Android APT】注解处理器 ( 配置注解依赖、支持的注解类型、Java 版本支持 )
文章目录 一.注解处理器 依赖 编译时注解 二.设置 注解处理器 支持的注解类型 三.设置 注解处理器 支持的 Java 版本 四.博客资源 Android APT 学习进阶路径 : 推荐按照顺序阅读 ...
- 【Android 组件化】路由组件 ( 注解处理器获取被注解的节点 )
文章目录 一.设置支持的注解类型 二.注解处理器中打印日志 三.主应用中使用注解 四.注解处理器 获取注解节点 五.博客资源 组件化系列博客 : [Android 组件化]从模块化到组件化 [Andr ...
- 【Android 组件化】路由组件 ( 注解处理器中使用 JavaPoet 生成代码 )
文章目录 一.注解节点类型 二.JavaPoet 简介 三.注解处理器中使用 JavaPoet 生成代码 四.路由框架说明 五.博客资源 组件化系列博客 : [Android 组件化]从模块化到组件化 ...
- Android 注解与注解处理器简述
Android 注解与注解处理器简述 前言 正文 一.注解 ① 注解类型 ② 注解生命周期 ③ 注解参数 二.注解处理器 ① 注册 ② 配置 三.使用 ① 接口 ② 反射 ③ 使用 ④ 强化 四.源码 ...
- Android 注解处理器使用攻略
上一篇写了JavaPoet使用攻略,了解了JavaPoet用法.那么我们就可以结合今天的Annotation Processing Tool(APT)来自定义注解处理器. 注解处理器简单解释就是收集我 ...
- 秒懂Android注解处理器(Android Annotation Processor)
[版权申明]非商业目的可自由转载 博文地址:https://blog.csdn.net/ShuSheng0007/article/details/90734159 出自:shusheng007 文章目 ...
- 【错误记录】Android 编译时技术版本警告 ( 注解处理器与主应用支持的 Java 版本不匹配 )
文章目录 一.报错信息 二.问题分析 三.解决方案 一.报错信息 在使用 Android 编译时技术 , 涉及 编译时注解 , 注解处理器 ; 开发注解处理器后 , 编译报如下警告 ; 该警告不会影响 ...
- java自定义注解处理器_Android自定义注解处理器
前言 在android开发中有很多运用到注解处理器(annotation processing)的框架,如常见的Butterknife,Dagger2,EventBus等,运用这些注解处理器框架大大简 ...
最新文章
- python多进程详解
- HttpWebRequest.GetResponse() raises exception when http status code 400 (bad request) is returned
- 从Zero到Hero,一文掌握Python关键代码
- Robots on a Grid CodeForces - 1335F(拓扑排序+正反建图+判环)
- ​分布式数据库技术基础:数据分布介绍
- (建议收藏)前端面试必问的十六条HTTP网络知识体系
- 华为发布全新一代OceanStor存储Pacific系列,打造海量数据存储新标杆
- dataguard如何实现切换_ORACLE dataguard 切换
- 中的live_张杰两首歌曲连唱彰显LIVE实力 青春演说温暖人心
- 常用Quartz cron表达式例子
- 黑白照片转换成彩色照片(无需任何编程)
- Chrome的隐身模式
- 俄罗斯方块30周年 设计者忆当年
- JavaScript BOM和DOM部分
- 服务器的原点和限位信号,CANopen--基于DS402协议的伺服电机原点回零模式实现
- input数字输入框可以输入e的原因
- 直接插入排序以及折半插入排序详解
- vb程序设计——多态
- ubuntu系统下安装GTX1660ti的Nvidia驱动
- QT:简单的小游戏开发日记