1、Android注解快速入門和實(shí)用解析
2、Annotation Processor重復(fù)造輪子ARouter
3、編譯時(shí)注解帶你封裝微信支付
Annotation Processor Tool (APT)是一種處理注釋的工具,它對源代碼文件進(jìn)行檢測找出其中的Annotation,根據(jù)注解自動(dòng)生成代碼,如果想要自定義的注解處理能夠正常運(yùn)行,必須要通過APT工具來進(jìn)行處理。Java 的Annotation Processor是非常有用的功能,很多常用的庫和框架都使用了 Annotation Processor 來生成代碼,比如Butter Knife 、ARouter等都是用來生成 代碼的。
結(jié)構(gòu)化語言
<html>
<body>
</body>
</html>
比如Html就是典型的Element 結(jié)構(gòu)化語言,body標(biāo)簽不能卸載html之上,因?yàn)檫@些是有規(guī)定的,那么Java也是一種結(jié)構(gòu)體語言:
package com.wfy.common; // PackageElement 包元素節(jié)點(diǎn)
public class Order implements Serializable { // TypeElement 類或接口元素/節(jié)點(diǎn)
int orderId = 99; //VariableElement 字段,枚舉常量,方法或構(gòu)造函數(shù)參數(shù),局部變量或異常參數(shù)元素/節(jié)點(diǎn)
@Override
public String toString() {//ExecutableElement 方法元素/節(jié)點(diǎn)
return "Order{" +
"orderId=" + orderId +
'}';
}
}
java也是一種結(jié)構(gòu)體語言,來說說它的各個(gè)節(jié)點(diǎn)類:
- PackageElement 一個(gè)包程序元素節(jié)點(diǎn);
- TypeElement 類或接口元素節(jié)點(diǎn);
- VariableElement 字段,枚舉常量,方法和構(gòu)造函數(shù)參數(shù),局部變量或異常參數(shù)元素節(jié)點(diǎn);
- ExecutableElement 方法元素節(jié)點(diǎn);
- TypeParameterElement 表示泛型類,接口,方法或構(gòu)造函數(shù)元素的形式類型參數(shù)節(jié)點(diǎn)。
常用API
| 屬性名 | 解釋 |
|---|---|
| getEnclosedElements() | 返回該Element直接包含的子元素,比如類可以包含構(gòu)造函數(shù)Element(PackageElement ),字段Element(VariableElement ),方法Element(ExecutableElement)等。 |
| getEnclosingElement() | 返回該Element的父Element, 如果Element是一個(gè)頂層類或者接口(TypeElement),就返回包Element(PackageElement),如果是包Element,就返回null。 |
| getKind() | 返回Element的類型,判斷是哪種Element。 |
| getModifiers() | 獲取修飾關(guān)鍵字,public static final等關(guān)鍵字。 |
| getSimpleName() | 獲取Element的名字,不帶包名。 |
| getQuelfierdName() | 獲取全名,如果是類的話,包含完整的包名路徑。 |
| getParamters() | 獲取方法的參數(shù)元素,每個(gè)元素是一個(gè)VariableElement。 |
| getRetrunType() | 獲取方法的返回值類型。 |
| getConstantValue() | 如果屬性變量被final修飾,則可以使用該方法獲取它的值。 |
最后我在介紹幾個(gè)常用的類:
- ElementVisitor<R,P> 當(dāng)你使用APT,檢測到指定的的Element節(jié)點(diǎn)時(shí)你可以使用這個(gè)類去訪問這個(gè)Element節(jié)點(diǎn)的信息,它的實(shí)現(xiàn)類:
AbstractElementVisitor6, AbstractElementVisitor7, ElementKindVisitor6, ElementKindVisitor7, ElementScanner6, ElementScanner7, SimpleElementVisitor6, SimpleElementVisitor7 - AnnotationValueVisitor<R,P> 當(dāng)你使用ElementVisitor訪問到指定的的AnnotationElement節(jié)點(diǎn)時(shí)你可以使用這個(gè)類去訪問這個(gè)AnnotationElement節(jié)點(diǎn)的信息,它的實(shí)現(xiàn)類:
AbstractAnnotationValueVisitor6, AbstractAnnotationValueVisitor7, SimpleAnnotationValueVisitor6, SimpleAnnotationValueVisitor7 - TypeVisitor<R, P> 你使用ElementVisitor訪問到指定的的 TypeElement節(jié)點(diǎn)時(shí)你可以使用這個(gè)類去訪問這個(gè)TypeElement節(jié)點(diǎn)的信息,它的實(shí)現(xiàn)類:AbstractTypeVisitor6, SimpleTypeVisitor6, TypeKindVisitor6
Annotation Processor Tool (APT)
是一種處理注解的工具,它對源代碼文件進(jìn)行檢測找出其中的Annotation,根據(jù)注解自動(dòng)生成代碼,如果想要自定義注解處理器能夠正常運(yùn)行,必須要通過APT工具進(jìn)行處理。
本文通過如何使用注解來自動(dòng)生成Music和User類,這些類并沒有實(shí)際的使用意義;
注解:
Android Studio 中創(chuàng)建一個(gè) Java module,名字為 Annotation。在這個(gè)模塊中創(chuàng)建這個(gè)自定義的 MyAnnotation注解類:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 只在源代碼中存在,編譯后沒有在字節(jié)碼中,所以最最終的運(yùn)行時(shí)是沒有影響的
*
*/
@Target(ElementType.TYPE) //作用于類上
@Retention(RetentionPolicy.SOURCE)//在源碼可見
public @interface MyAnnotation {
}
注解處理器
注解處理器的功能就是用來讀取代碼中的注解然后來生成相關(guān)的代碼。
創(chuàng)建一個(gè) Java module 名字為 “compiler”。 該模塊在編譯的時(shí)候,來獲取哪些類使用了MyAnnotation 注解,并生成代碼。該模塊并不在 Android 項(xiàng)目中引用,只存在于編譯的時(shí)候。所以這個(gè)模塊的 Java 版本號可以隨意指定(Java 8 、9)
@SupportedAnnotationTypes("com.xx.lib.MyAnnotation")
public final class MyProcessor extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment env) {
Collection<? extends Element> annotatedElements = env.getElementsAnnotatedWith(MyAnnotation.class);
//轉(zhuǎn)換類型
List<TypeElement> types = new ImmutableList.Builder<TypeElement>()
.addAll(ElementFilter.typesIn(annotatedElements))
.build();
for (TypeElement type : types) {
processType(type);
}
return true;
}
private void processType(TypeElement type) {
String className = generatedClassName(type);
StringBuffer sb = new StringBuffer();
sb.append("public class " + type.getSimpleName() + "{");
sb.append("private int age;");
sb.append("private String name;");
sb.append("}");
String source = sb.toString();
writeSourceFile(className, source, type);
}
private void writeSourceFile(String className, String text, TypeElement originatingType) {
try {
JavaFileObject sourceFile = processingEnv.getFiler().createSourceFile(className, originatingType);
Writer writer = sourceFile.openWriter();
try {
writer.write(text);
} finally {
writer.close();
}
} catch (IOException e) {
}
}
private String generatedClassName(TypeElement type) {
String name = type.getSimpleName().toString();
while (type.getEnclosingElement() instanceof TypeElement) {
type = (TypeElement) type.getEnclosingElement();
name = type.getSimpleName() + "_" + name;
}
String pkg = TypeUtil.packageNameOf(type);
String dot = pkg.isEmpty() ? "" : ".";
return pkg + dot + name;
}
}
對于該類有幾點(diǎn)要求:
1. 需要繼承至 AbstractProcessor
2. 需要使用類的全稱(包含包名)來指定其支持的注解類型(com.xx.lib.MyAnnotation)
3. 實(shí)現(xiàn) process() 函數(shù),在該函數(shù)中來處理所支持的注解類型并生成需要的代碼。
如果沒有其他處理器需要繼續(xù)處理該注解,則 process() 返回 true。針對我們這個(gè)情況,只有 MyProcessor需要處理 MyAnnotation注解,所以該函數(shù)返回 true。
注解處理器類編寫完后,需要?jiǎng)?chuàng)建一個(gè) java META_INF 文件來告訴系統(tǒng)具有注解處理功能。Java 代碼在編譯的時(shí)候,系統(tǒng)編譯器會查找所有的 META_INF 中的注冊的注解處理器來處理注解。
在 Android studio 的 compiler 項(xiàng)目中創(chuàng)建如下目錄:
compiler/src/main/resources/META_INF/services
在 services 目錄下面創(chuàng)建一個(gè)名字為 “javax.annotation.processing.Processor” 的文本文件:
該文件中每行一個(gè)注解處理器的全名:
com.xx.compiler.MyProcessor
注解處理器就創(chuàng)建好了
在 Android Studio 中使用
在 app/build.gradle 中添加前面創(chuàng)建的兩個(gè)模塊為依賴項(xiàng):
compileOnly project(':Annotation')
annotationProcessor project(':compiler')
注意上面 MyAnnotation項(xiàng)目使用的是 compileOnly 依賴,這是由于 compileOnly 中的代碼只在編譯的時(shí)候存在,并不會打包到最終的應(yīng)用中去,所以可以使用 compileOnly 。compiler 項(xiàng)目為注解編譯器,通過使用 插件來指定 annotationProcessor 。
接下來就可以使用了:
@MyAnnotation
public class User {
}