安卓註解處理器-processor
最近在學習安卓開源框架發現,很多的開源框架都使用到了註解處理器,例如EventBus3.0。本文通過一個簡單的Demo來介紹如何使用註解處理器。如果喜歡的話,歡迎大家給star。
Demo需求描述
使用者通過執行一個傳入引數為A(類物件)的靜態方法,該方法會最終把引數A中加了特定註解的所有方法執行一遍。
需求實現
整個專案分為四個部分:
- 註解–要使用的註解型別,這部分通常也可以放在lib中;
- 註解處理器–要對註解進行處理的邏輯,包括收集有特定註解型別的方法資訊以及生成特定的java檔案;
- lib–封裝合適的介面,供具體呼叫方呼叫;
- sample–具體的呼叫方邏輯。
首先新建一個安卓工程,點選執行展示的是hello world。
註解
在上述工程中new->Module->Java Library,新建一個Java Library Module,命名為annotation。在該Module下建立一個檔案AnnotationTest.java,
AnnotationTest.java裡面程式碼如下:
@Retention(RetentionPolicy.CLASS) @Target(ElementType.METHOD) public @interface AnnotationTest { String name() default "test"; }
1、註解@Retention按生命週期來劃分可分為3類:
- RetentionPolicy.SOURCE:註解只保留在原始檔,當Java檔案編譯成class檔案的時候,註解被遺棄;
- RetentionPolicy.CLASS:註解被保留到class檔案,當jvm載入class檔案時候被遺棄,這是預設的生命週期;
- RetentionPolicy.RUNTIME:註解不僅被儲存到class檔案中,jvm載入class檔案之後,仍然存在。
這3個生命週期分別對應於:Java原始檔(.java檔案) —> .class檔案 —> 記憶體中的位元組碼。
2、註解@Target表示修飾的註解能使用的範圍,ElementType.METHOD表示@AnnotationTest註解只能作用在方法上。
註解處理器
參照上部分,在工程中new->Module->Java Library,新建一個Java Library Module, 在該Module下建立一個檔案ProcessorTest.java。在該Module下的build.gradle的dependencies中新增如下配置:
// 自動為processor註冊
implementation 'com.google.auto.service:auto-service:1.0-rc2'
// 該Module依賴上部分建立的annotation Module
implementation project(':annotation')
com.google.auto.service:auto-service:1.0-rc2依賴的作用是為註解處理器自動註冊,它會生成META-INF資料夾。
註解處理器ProcessorTest的定義如下,其中@AutoService(Processor.class)就是build.gradle中加的依賴幫助其自動註冊。
@AutoService(Processor.class) // 自動為ProcessorTest註冊,生成META-INF檔案
public class ProcessorTest extends AbstractProcessor{
註解處理器ProcessorTest主要包含以下幾個部分:
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
mMessager = processingEnvironment.getMessager();
mFiler = processingEnvironment.getFiler();
}
init方法是註解處理器會自動呼叫的初始化方法,其中mFiler是用來生成java原始檔的工具,mMessager是用來列印日誌的,它們的具體使用會在後面介紹。
@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> supportAnnotationTypes = new HashSet<>();
supportAnnotationTypes.add(AnnotationTest.class.getCanonicalName());
return supportAnnotationTypes;
}
getSupportedAnnotationTypes()方法返回該註解處理器支援的註解型別,這裡返回的就是我們之前宣告的新的註解型別@AnnotationTest。
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
getSupportedSourceVersion()方法一般就按照上述實現就行。
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnvironment) {
// 列印日誌
mMessager.printMessage(Diagnostic.Kind.NOTE, "process start");
Map<String, List<String>> collectInfos = new HashMap<>();
for (TypeElement annotation: annotations){
Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(annotation);
for (Element element: elements){
// 檢查element是否符合我們定義的規範
if (!checkValid(element)){
mMessager.printMessage(Diagnostic.Kind.NOTE, "checkValid not pass");
return false;
}else {
ExecutableElement executableElement = (ExecutableElement) element;
// 獲取被註解的方法所在的類
TypeElement typeElement = (TypeElement) executableElement.getEnclosingElement();
// 獲取類的全名,包括包名
String classFullName = typeElement.getQualifiedName().toString();
// 被註解的方法的名字
String methodName = executableElement.getSimpleName().toString();
List<String> methods = collectInfos.get(classFullName);
if (methods == null){
methods = new ArrayList<>();
collectInfos.put(classFullName, methods);
}
methods.add(methodName);
}
}
}
for (Map.Entry<String, List<String>> entry: collectInfos.entrySet()){
mMessager.printMessage(Diagnostic.Kind.NOTE, entry.getKey());
// 生成java原始檔
createJavaFile(entry.getKey(), entry.getValue());
}
return true;
}
process方法是我們的主要邏輯處理的地方,主要邏輯就是收集所有有@AnnotationTest註解的方法以及其所在的類資訊,然後根據每個類資訊,生成一個新的類檔案,並在新的類檔案的特定方法中呼叫所有關聯的註解方法。生成java原始檔將使用Filer物件,具體如何使用請下載demo看原始碼。
注:
1、當你點選buid project時,註解處理器將會執行,而Messager物件打印出來的日誌資訊可以在Gradle Console視窗中看到。
2、如果你在該Module中使用中文註解,因為該Module為java library,可能會報GBK編碼錯誤,解決辦法是在該Module的build.gradle中新增如下程式碼:
//指定編譯的編碼
tasks.withType(JavaCompile){
options.encoding = "UTF-8"
}
Lib
在工程中new->Module->Android Library ,新建一個Android Library Module,封裝介面給呼叫方使用,具體邏輯請參考demo。
最終該demo的功能是點選Hello world文字,會依此執行MainActivity中使用@AnnotationTest註解的方法。