【Android APT】注解处理器 ( Element 注解节点相关操作 )
文章目錄
- 一、獲取被 注解 標(biāo)注的節(jié)點(diǎn)
- 二、Element 注解節(jié)點(diǎn)類型
- 三、VariableElement 注解節(jié)點(diǎn)相關(guān)操作
- 四、注解處理器 完整代碼示例
- 五、博客資源
Android APT 學(xué)習(xí)進(jìn)階路徑 : 推薦按照順序閱讀 , 從零基礎(chǔ)到開發(fā)簡易 ButterKnife 注解框架的學(xué)習(xí)路徑 ;
- 【Java 注解】注解簡介及作用
- 【Java 注解】自定義注解 ( 注解屬性定義與賦值 )
- 【Java 注解】自定義注解 ( 元注解 )
- 【Java 注解】自定義注解 ( 注解解析 )
- 【Java 注解】自定義注解 ( 使用注解實(shí)現(xiàn)簡單測試框架 )
- 【Android APT】編譯時技術(shù) ( ButterKnife 原理分析 )
- 【Android APT】編譯時技術(shù) ( 編譯時注解 和 注解處理器 依賴庫 )
- 【Android APT】編譯時技術(shù) ( 開發(fā)編譯時注解 )
- 【Android APT】注解處理器 ( 注解標(biāo)注 與 初始化方法 )
- 【Android APT】注解處理器 ( 配置注解依賴、支持的注解類型、Java 版本支持 )
- 【Android APT】注解處理器 ( Element 注解節(jié)點(diǎn)相關(guān)操作 )
上一篇博客 【Android APT】注解處理器 ( 配置注解依賴、支持的注解類型、Java 版本支持 ) 中 為 注解處理器 Module 添加了 編譯時注解 Module 依賴 , 并設(shè)置了支持該注解處理器 支持的 注解類型 , 和 支持的 Java 版本 ;
本篇博客開發(fā) 注解處理器 的 處理注解 , 生成代碼的核心邏輯 ;
一、獲取被 注解 標(biāo)注的節(jié)點(diǎn)
處理注解的核心邏輯在 AbstractProcessor 中的 process 方法中實(shí)現(xiàn) ;
@AutoService(Processor.class) public class Compiler extends AbstractProcessor {/*** 搜索 Android 代碼中的 BindView 注解* 并生成相關(guān)代碼* @param annotations* @param roundEnv* @return*/@Overridepublic boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {return false;} }先獲取被注解標(biāo)注的節(jié)點(diǎn) , 搜索 BindView , 調(diào)用 process 方法的 RoundEnvironment roundEnv 參數(shù)的 getElementsAnnotatedWith 方法 , 即可搜索到整個 Module 中所有使用了 BindView 注解的元素 ;
// 搜索 BindView , 將 BindView 注解放在什么元素上 , 得到的就是相應(yīng)類型的元素 // 根據(jù) 注解類型 獲取 被該注解類型 標(biāo)注的元素 , 元素可能是類 , 方法 , 字段 ; // 通過 getElementsAnnotatedWith 方法可以搜索到整個 Module 中所有使用了 BindView 注解的元素 Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(BindView.class);將 BindView 注解放在什么元素上 , 得到的就是相應(yīng)類型的元素 , 根據(jù) 注解類型 獲取 被該注解類型 標(biāo)注的元素 , 元素可能是類 , 方法 , 字段 ;
在 app 模塊中 , 只有 MainActivity 中的一個 屬性字段 使用 BindView 注解 , 調(diào)用 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);} }假設(shè)在 若干 Activity 中 , 若干位置 , 使用了 BindView 注解 , 那么在獲取的所有使用了 BindView 注解的字段 Set<? extends Element> elements 中裝載了所有的使用了該注解的字段 , 這些字段來自不同的 Activity 中 ;
這就需要將 Set<? extends Element> elements 中的 字段 按照 Activity 上下文進(jìn)行分組 , 以便生成代碼 ;
這樣每個 Activity 界面都對應(yīng)若干個 Set<? extends Element> elements 中的元素 ;
二、Element 注解節(jié)點(diǎn)類型
使用注解標(biāo)注的 Element 節(jié)點(diǎn)類型 :
ExecutableElement : 使用注解的 方法 節(jié)點(diǎn)類型 ;
VariableElement : 使用注解的 字段 節(jié)點(diǎn)類型 , 類的成員變量 ;
TypeElement : 使用注解的 類 節(jié)點(diǎn)類型 ;
PackageElement : 使用注解的 包 節(jié)點(diǎn)類型 ;
上述 444 個類都是 javax.lang.model.element.Element 的子類 ;
@BindView 注解標(biāo)注的都是 Activity 中的成員字段 , 當(dāng)前獲取 Set<? extends Element> elements 集合中的節(jié)點(diǎn)都是 Field 字段對應(yīng)的 VariableElement 類型的節(jié)點(diǎn) ;
三、VariableElement 注解節(jié)點(diǎn)相關(guān)操作
遍歷上述 VariableElement 類型節(jié)點(diǎn)集合 , 將其中的元素按照 Activity 進(jìn)行分組 , 將分組結(jié)果放到 HashMap<String, HashSet<VariableElement>> elementMap 鍵值對 Map 集合中 ;
要分組放置 注解節(jié)點(diǎn) 的 HashMap<String, HashSet<VariableElement>> elementMap 鍵值對 Map 集合 , 其中 " 鍵 " 是 注解標(biāo)注的成員字段所在的 Activity 類的全類名 , " 值 " 是該 Activity 中所有使用 @BindView 注解的成員字段集合 ;
// @BindView 注解標(biāo)注的都是 Activity 中的成員字段, // 上述 elements 中的元素都是 VariableElement 類型的節(jié)點(diǎn) HashMap<String, HashSet<VariableElement>> elementMap = new HashMap<>();給定 VariableElement 注解節(jié)點(diǎn) , 獲取該節(jié)點(diǎn)的上一級節(jié)點(diǎn) , 注解節(jié)點(diǎn)是 VariableElement , 成員字段節(jié)點(diǎn) , 其上一級節(jié)點(diǎn)是就是 Activity 類對應(yīng)的 類節(jié)點(diǎn) TypeElement , 通過調(diào)用 VariableElement.getEnclosingElement 方法獲取上一級節(jié)點(diǎn) , 類型是 TypeElement ;
// 將注解節(jié)點(diǎn)類型強(qiáng)轉(zhuǎn)為 VariableElement 類型 VariableElement ve = (VariableElement) element;// 獲取該注解節(jié)點(diǎn)對應(yīng)的成員變量類名 // 先獲取該注解節(jié)點(diǎn)的上一級節(jié)點(diǎn) , 注解節(jié)點(diǎn)是 VariableElement , 成員字段節(jié)點(diǎn) // 上一級節(jié)點(diǎn)是就是 Activity 類節(jié)點(diǎn)對應(yīng)的 類節(jié)點(diǎn) TypeElement TypeElement te = (TypeElement) ve.getEnclosingElement();獲取 TypeElement 注解節(jié)點(diǎn) 的全類名 , 調(diào)用 TypeElement.getQualifiedName 方法獲取 , 如果只獲取類名 , 不包含完整包名的話 , 調(diào)用 TypeElement.getSimpleName 方法獲取 ;
// 獲取 Activity 的全類名 String className = te.getQualifiedName().toString();根據(jù)從 VariableElement 對象中獲取的上一級節(jié)點(diǎn)的類名 , 對 Set<? extends Element> elements 集合中的 VariableElement 類型元素進(jìn)行分組 , 同一個 Activity 中對應(yīng)的 注解節(jié)點(diǎn) 對象放在一個 HashSet<VariableElement> 集合中 , 然后放到 HashMap<String, HashSet<VariableElement>> elementMap 中 , 鍵是 Activity 全類名 ;
具體的集合操作參考下面的源碼示例 ;
部分代碼示例 :
/*** 搜索 Android 代碼中的 BindView 注解* 并生成相關(guān)代碼* @param annotations* @param roundEnv* @return*/@Overridepublic boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {// 搜索 BindView , 將 BindView 注解放在什么元素上 , 得到的就是相應(yīng)類型的元素// 根據(jù) 注解類型 獲取 被該注解類型 標(biāo)注的元素 , 元素可能是類 , 方法 , 字段 ;// 通過 getElementsAnnotatedWith 方法可以搜索到整個 Module 中所有使用了 BindView 注解的元素Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(BindView.class);// @BindView 注解標(biāo)注的都是 Activity 中的成員字段,// 上述 elements 中的元素都是 VariableElement 類型的節(jié)點(diǎn)HashMap<String, HashSet<VariableElement>> elementMap = new HashMap<>();// 遍歷 elements 注解節(jié)點(diǎn) , 為節(jié)點(diǎn)分組for (Element element : elements){// 將注解節(jié)點(diǎn)類型強(qiáng)轉(zhuǎn)為 VariableElement 類型VariableElement ve = (VariableElement) element;// 獲取該注解節(jié)點(diǎn)對應(yīng)的成員變量類名// 先獲取該注解節(jié)點(diǎn)的上一級節(jié)點(diǎn) , 注解節(jié)點(diǎn)是 VariableElement , 成員字段節(jié)點(diǎn)// 上一級節(jié)點(diǎn)是就是 Activity 類節(jié)點(diǎn)對應(yīng)的 類節(jié)點(diǎn) TypeElementTypeElement te = (TypeElement) ve.getEnclosingElement();// 獲取 Activity 的全類名String className = te.getQualifiedName().toString();mMessager.printMessage(Diagnostic.Kind.NOTE, "TypeElement : " + className + " , VariableElement : " + ve.getSimpleName());// 獲取 elementMap 集合中的 Activity 的全類名對應(yīng)的 VariableElement 節(jié)點(diǎn)集合// 如果是第一次獲取 , 為空 ,// 如果之前已經(jīng)獲取了該 Activity 的全類名對應(yīng)的 VariableElement 節(jié)點(diǎn)集合, 那么不為空HashSet<VariableElement> variableElements = elementMap.get(className);if (variableElements == null){variableElements = new HashSet<>();// 創(chuàng)建之后 , 將集合插入到 elementMap 集合中elementMap.put(className, variableElements);}// 將本節(jié)點(diǎn)插入到 HashSet<VariableElement> variableElements 集合中variableElements.add(ve);}return false;}編譯時 注解處理器 打印的內(nèi)容 : 在 Build 面板 , " Build Output " 選項(xiàng)卡中輸入編譯相關(guān)信息 , 其中 " Task :app:compileDebugJavaWithJavac " 任務(wù)輸出 注解處理器 相關(guān)日志 ;
四、注解處理器 完整代碼示例
注解處理器 完整代碼示例 :
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;/*** 初始化注解處理器相關(guān)工作* @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 注解* 并生成相關(guān)代碼* @param annotations* @param roundEnv* @return*/@Overridepublic boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {// 搜索 BindView , 將 BindView 注解放在什么元素上 , 得到的就是相應(yīng)類型的元素// 根據(jù) 注解類型 獲取 被該注解類型 標(biāo)注的元素 , 元素可能是類 , 方法 , 字段 ;// 通過 getElementsAnnotatedWith 方法可以搜索到整個 Module 中所有使用了 BindView 注解的元素Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(BindView.class);// @BindView 注解標(biāo)注的都是 Activity 中的成員字段,// 上述 elements 中的元素都是 VariableElement 類型的節(jié)點(diǎn)HashMap<String, HashSet<VariableElement>> elementMap = new HashMap<>();// 遍歷 elements 注解節(jié)點(diǎn) , 為節(jié)點(diǎn)分組for (Element element : elements){// 將注解節(jié)點(diǎn)類型強(qiáng)轉(zhuǎn)為 VariableElement 類型VariableElement ve = (VariableElement) element;// 獲取該注解節(jié)點(diǎn)對應(yīng)的成員變量類名// 先獲取該注解節(jié)點(diǎn)的上一級節(jié)點(diǎn) , 注解節(jié)點(diǎn)是 VariableElement , 成員字段節(jié)點(diǎn)// 上一級節(jié)點(diǎn)是就是 Activity 類節(jié)點(diǎn)對應(yīng)的 類節(jié)點(diǎn) TypeElementTypeElement te = (TypeElement) ve.getEnclosingElement();// 獲取 Activity 的全類名String className = te.getQualifiedName().toString();mMessager.printMessage(Diagnostic.Kind.NOTE, "TypeElement : " + className + " , VariableElement : " + ve.getSimpleName());// 獲取 elementMap 集合中的 Activity 的全類名對應(yīng)的 VariableElement 節(jié)點(diǎn)集合// 如果是第一次獲取 , 為空 ,// 如果之前已經(jīng)獲取了該 Activity 的全類名對應(yīng)的 VariableElement 節(jié)點(diǎn)集合, 那么不為空HashSet<VariableElement> variableElements = elementMap.get(className);if (variableElements == null){variableElements = new HashSet<>();// 創(chuàng)建之后 , 將集合插入到 elementMap 集合中elementMap.put(className, variableElements);}// 將本節(jié)點(diǎn)插入到 HashSet<VariableElement> variableElements 集合中variableElements.add(ve);}return false;} }五、博客資源
博客源碼 :
-
GitHub : https://github.com/han1202012/APT
-
CSDN :
總結(jié)
以上是生活随笔為你收集整理的【Android APT】注解处理器 ( Element 注解节点相关操作 )的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Android APT】注解处理器 (
- 下一篇: 【错误记录】Android Studio