介紹
APT就是(Annotation Processing Tool )的簡稱渗常,就是可以在代碼編譯期間對注解進(jìn)行處理,并且生成Java文件拖云,減少手動的代碼輸入。
代表框架:
Dagger2
ButterKnife
EventBus
ARouter
作用
使用APT的優(yōu)點(diǎn)就是方便跃惫、簡單,可以少些很多重復(fù)的代碼坑雅。
APT處理要素
注冊處理器(AutoService) + 注解處理器(AbstractProcessor) + 代碼生成(javapoet)
1辈挂、 注冊處理器
注冊處理器有2種方式
一種是AutoService
首先引入
implementation 'com.google.auto.service:auto-service:1.0-rc2'
annotationProcessor 'com.google.auto.service:auto-service:1.0-rc2'
然后再AbstractProcessor類中加上注解@AutoService(Processor.class)
這樣就注冊完成了
第二種是添加文件
。
2裹粤、 注解處理器(AbstractProcessor)
創(chuàng)建一個繼承AbstractProcessor 類就可以了终蒂,在process方法里,獲取我們需要的哪些注解遥诉。
public class BindViewProcessor extends AbstractProcessor {
public class BindViewProcessor extends AbstractProcessor {
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
}
/**
* 指定注解
*/
@Override
public Set<String> getSupportedAnnotationTypes() {
HashSet<String> supportTypes = new LinkedHashSet<>();
supportTypes.add(BindView.class.getCanonicalName());
return supportTypes;
}
/**
* 用來指定你使用的Java版本
*/
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
/**
* 掃描注解處理
*/
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
return true;
}
3拇泣、 代碼生成
代碼生成也有2種方式來實(shí)現(xiàn)
1.StringBuilder
來實(shí)現(xiàn),大概例子是這樣的矮锈,所有的代碼都是用字符串拼接起來霉翔,生成的代碼格式也很亂,而且很容易寫錯苞笨。
2.另外一種是使用javapoet
生成代碼债朵,生成的代碼會自動排版,javapoet詳細(xì)用法
可以看下兩者的比較瀑凝。
/**
* 使用StringBuilder創(chuàng)建類序芦、而且還需要自己手寫導(dǎo)入包,這邊沒寫
*/
public String generateJavaCode() {
StringBuilder builder = new StringBuilder();
builder.append("package ").append(mPackageName).append(";\n\n");
builder.append('\n');
builder.append("public class ").append(mBindingClassName);
builder.append(" {\n");
generateMethods(builder);//創(chuàng)建方法
builder.append('\n');
builder.append("}\n");
return builder.toString();
}
/**
* 使用javapoet創(chuàng)建類
*/
public TypeSpec generateJavaCode2() {
TypeSpec bindingClass = TypeSpec.classBuilder(mBindingClassName)
.addModifiers(Modifier.PUBLIC)
.addMethod(generateMethods2())//創(chuàng)建方法
.build();
return bindingClass;
}
/**
* 使用StringBuilder創(chuàng)建方法
*/
private void generateMethods(StringBuilder builder) {
builder.append("public void bind(" + mTypeElement.getQualifiedName() + " target ) {\n");
for (int id : mapId.keySet()) {
VariableElement element = mapId.get(id);
String name = element.getSimpleName().toString();
builder.append("target." + name).append(" = ");
builder.append("target.findViewById( " + id + ");\n");
}
builder.append(" }\n");
}
/**
* 使用javapoet創(chuàng)建方法
*/
private MethodSpec generateMethods2() {
ClassName target = ClassName.bestGuess(mTypeElement.getQualifiedName().toString());
MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder("bind")
.addModifiers(Modifier.PUBLIC)
.returns(void.class)
.addParameter(target, "target");
for (int id : mapId.keySet()) {
VariableElement element = mapId.get(id);
String name = element.getSimpleName().toString();
methodBuilder.addCode("target." + name + " = " + "target.findViewById( " + id + ");");
}
return methodBuilder.build();
}
這三步完成我們就可以生成我們要的代碼了粤咪。
javapoet詳細(xì)用法
獲取注解對象
1谚中、運(yùn)行時注解: 通過 反射 機(jī)制獲取注解對象,會損耗性能寥枝,常用的框架有retrofit
2宪塔、編譯期注解: 通過 APT 方式獲取注解對象,不會造成性能損耗囊拜。常用的框架Dagger2, ButterKnife某筐、EventBus、ARouter
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.FIELD)
public @interface BindView {
}
@Retention(RetentionPolicy.CLASS)
:表示編譯時注解
@Target(ElementType.FIELD)
:表示注解范圍為類成員(構(gòu)造方法艾疟、方法来吩、成員變量)
注解 @Target代表意思
@Target(ElementType.TYPE) 接口、類蔽莱、枚舉弟疆、注解
@Target(ElementType.FIELD) 字段、枚舉的常量
@Target(ElementType.METHOD) 方法
@Target(ElementType.PARAMETER) 方法參數(shù)
@Target(ElementType.CONSTRUCTOR) 構(gòu)造函數(shù)
@Target(ElementType.LOCAL_VARIABLE) 局部變量
@Target(ElementType.ANNOTATION_TYPE) 注解
@Target(ElementType.PACKAGE) 包
element 的概念
表示一個程序元素盗冷,比如包怠苔、類或者方法。
element 包含有
PackageElement
表示一個包程序元素
TypeElement
表示一個類或接口程序元素
VariableElement
表示一個字段仪糖、enum 常量柑司、方法或構(gòu)造方法參數(shù)迫肖、局部變量或異常參數(shù)
ExecutableElement
表示某個類或接口的方法、構(gòu)造方法或初始化程序(靜態(tài)或?qū)嵗┰艹郏ㄗ⒔忸愋驮?br>
TypeParameterElement
表示一般類蟆湖、接口、方法或構(gòu)造方法元素的泛型參數(shù)
如下圖
public class Foo { // TypeElement 類型元素
private int a; // VariableElement 變量元素
private Foo other; // VariableElement 變量元素
public Foo() { // ExecutableElement 可執(zhí)行元素
}
public void setA(int newA ) { // newA 代表是一個 VariableElement)
}
}
Element
的源碼玻粪,源碼解析隅津。
public interface Element extends javax.lang.model.AnnotatedConstruct {
/**
* 返回該元素定義的類型。
* 泛型元素定義了一系列類型劲室,而不僅僅是一個類型伦仍。如果這是一個泛型元素,則返回一個原型
* 類型很洋。這是元素在對應(yīng)于它自己的正式類型參數(shù)的類型變量上的調(diào)用充蓝。例如,對于泛型類元素
* C<N extends Number>喉磁,返回參數(shù)化類型C<N>谓苟。類型實(shí)用程序接口有更一般的方法來獲取元
* 素定義的所有類型的范圍。
*/
TypeMirror asType();
/**
* 返回該元素的類型
*/
ElementKind getKind();
/**
* 返回該元素的修飾符,包括注解.
* 隱式修飾符也包含,比如接口方法中的public和static
*/
Set<Modifier> getModifiers();
/**
* 返回該元素的簡單名稱.泛型類型的名稱不包括對其正式類型參數(shù)的任何引用协怒。
* 舉例,java.util.Set<E>的簡單名稱是Set.
* 如果該元素代表的是未命名包,則返回一個空 Name.
* 如果代表的是構(gòu)造器,則返回<init>所對應(yīng)的Name.如果代表的是靜態(tài)代碼塊,則返回的是<clinit>
* 如果代表的是匿名類或者是初始代碼塊,則返回一個空 Name.
*/
Name getSimpleName();
/**
* 返回包圍該元素的最內(nèi)層的元素.
* 如果這個元素的聲明緊接在另一個元素的聲明中娜谊,則返回另一個元素。
* 如果這是頂級類型斤讥,則返回其包。
* 如果這是一個包湾趾,則返回null芭商。
* 如果這是類型參數(shù),則返回類型參數(shù)的泛型元素搀缠。
* 如果這是一個方法或構(gòu)造函數(shù)參數(shù)铛楣,則返回聲明該參數(shù)的可執(zhí)行元素。
*/
Element getEnclosingElement();
/**
* 返回該元素所包含的元素.
* 類或接口被認(rèn)為包含了它直接聲明的字段艺普、方法簸州、構(gòu)造函數(shù)和成員類型.包直接包含了頂級類和接
* 口,但不包含其子包歧譬。其他類型的元素目前不被認(rèn)為包含任何元素岸浑;然而,隨著這個API或編程語
* 言的發(fā)展瑰步,這些元素可能會改變
*/
List<? extends Element> getEnclosedElements();
/**
* 當(dāng)給定的參數(shù)和當(dāng)前類代表同一個元素時返回true,否則,返回false.
* 注意矢洲,元素的標(biāo)識涉及不能直接從元素的方法中訪問的隱式狀態(tài),包括關(guān)于不相關(guān)類型的存在的
* 狀態(tài)缩焦。即使“同一個”元素正在被建模读虏,由這些接口的不同實(shí)現(xiàn)創(chuàng)建的元素對象也不應(yīng)該期望相
* 等责静;這類似于通過不同的類加載器加載的同一個類文件的Class對象是不同的
*
*/
@Override
boolean equals(Object obj);
/**
* 基于Object.hashCode
*/
@Override
int hashCode();
/**
* 獲得直接聲明在該元素上的注解
* 如果要獲得繼承的注解,使用Elements#getAllAnnotationMirrors(Element)方法.
*/
@Override
List<? extends AnnotationMirror> getAnnotationMirrors();
@Override
<A extends Annotation> A getAnnotation(Class<A> annotationType);
<R, P> R accept(ElementVisitor<R, P> v, P p);
}