一、編譯時(shí)技術(shù)簡(jiǎn)介
APT ( Annotation Processing Tool ) 注解處理工具 ;
編譯時(shí)技術(shù) , 廣泛應(yīng)用在當(dāng)前主流框架中 , 如 JetPack 中的 DataBinding , Room , Navigatoion , 第三方 ButterKnife , ARouter 等框架 ;
編譯時(shí)技術(shù) 最重要的作用就是在編譯時(shí)可以 生成模板代碼 ;
由于生成代碼操作是在編譯時(shí)進(jìn)行的 , 不會(huì)對(duì)運(yùn)行時(shí)的性能產(chǎn)生影響 ;
程序的周期 :
源碼期 : 開(kāi)發(fā)時(shí) , 剛編寫(xiě)完 " .java " 代碼 , 還未編譯之前 , 就處于源碼期 ;
編譯期 : 程序由 java 源碼編譯成 class 字節(jié)碼文件 ;
運(yùn)行期 : 將字節(jié)碼文件加載到 Java 虛擬機(jī)中運(yùn)行 ;
編譯時(shí)技術(shù) APT 作用于 編譯期 , 在這個(gè)過(guò)程中使用該技術(shù) , 生成代碼 ;
編譯時(shí)技術(shù) 2 22 大核心要素 : 在編譯時(shí) , 執(zhí)行生成代碼的邏輯 , 涉及到兩個(gè)重要概念 ;
① 編譯時(shí)注解 ;
② 注解處理器 ;
舉例說(shuō)明 : 使用 ButterKnife 時(shí)會(huì)依賴(lài)兩個(gè)庫(kù) ,
dependencies {
implementation 'com.jakewharton:butterknife:10.2.3'
annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.3'
}
其中
com.jakewharton:butterknife:10.2.3 是 編譯時(shí)的注解 ,
com.jakewharton:butterknife-compiler:10.2.3 是 注解處理器 ;
二畏铆、ButterKnife 原理分析
使用 ButterKnife :
① 添加依賴(lài) :
dependencies {
implementation 'com.jakewharton:butterknife:10.2.3'
annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.3'
}
② Activity 中使用 ButterKnife :
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import butterknife.BindView;
import butterknife.ButterKnife;
public class MainActivity extends AppCompatActivity {
@BindView(R.id.tv1)
TextView tv1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
tv1.setText("ButterKnife");
}
}
BindView 注解分析 : 在 TextView tv1成員變量處添加了 @BindView(R.id.tv1) 注解 ;
@Target(FIELD) 元注解 : 表示其作用與類(lèi)的成員字段 ;
@Retention(RUNTIME) 元注解 : 表示該注解保留到運(yùn)行時(shí)階段 ;
int value() 注解屬性 : 只有一個(gè)注解屬性 , 并且屬性名是 value , 則使用注解時(shí) “value =” 可省略 ;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME) //注解的生命周期雷袋,保留時(shí)間到類(lèi)文件級(jí)別
@Target(ElementType.FIELD) //作用域在字段上
public @interface BindView {
int value();
}
TextView tv1 需要使用 findViewById 進(jìn)行賦值 , 在上述代碼中沒(méi)有寫(xiě) findViewById 相關(guān)的代碼 ; 肯定是在某個(gè)地方執(zhí)行了 findViewById 的方法 ;
ButterKnife.bind(this) 代碼就是執(zhí)行了 findViewById 方法 ;
ButterKnife 用到了編譯時(shí)技術(shù)會(huì)在項(xiàng)目編譯時(shí) , 會(huì)生成 MainActivity_ViewBinding 類(lèi) , 在該類(lèi)中 , 會(huì)查找添加了 @BindView 直接的成員變量 , 再獲取 注解屬性 value 的值 , 然后調(diào)用 findViewById 方法獲取組件并為成員變量賦值 ;
// Generated code from Butter Knife. Do not modify!
import android.view.View;
import android.widget.TextView;
import androidx.annotation.CallSuper;
import androidx.annotation.UiThread;
import butterknife.Unbinder;
import butterknife.internal.Utils;
import java.lang.IllegalStateException;
import java.lang.Override;
public class MainActivity_ViewBinding implements Unbinder {
private MainActivity target;
@UiThread
public MainActivity_ViewBinding(MainActivity target) {
this(target, target.getWindow().getDecorView());
}
@UiThread
public MainActivity_ViewBinding(MainActivity target, View source) {
this.target = target;
target.tv1= Utils.findRequiredViewAsType(source, R.id.tv1, "field 'tv1'", TextView.class);
}
@Override
@CallSuper
public void unbind() {
MainActivity target = this.target;
if (target == null) throw new IllegalStateException("Bindings already cleared.");
this.target = null;
target.hello = null;
}
}
三、ButterKnife 生成 Activity_ViewBinding 代碼分析
調(diào)用 ButterKnife 靜態(tài)方法 Unbinder bind(@NonNull Activity target) , 傳入 Activity 對(duì)象 , 在方法中調(diào)用了 ButterKnife 的 bind 方法 ;
在 bind 方法中 , 先獲取了 Activity 的類(lèi)對(duì)象 ,
Class<?> targetClass = target.getClass();
然后將類(lèi)對(duì)象傳入了 findBindingConstructorForClass 方法 ,
Constructor<? extends Unbinder> constructor = findBindingConstructorForClass(targetClass);
在 findBindingConstructorForClass 方法中 , 獲取了某個(gè)構(gòu)造方法 ,
Constructor<? extends Unbinder> bindingCtor = BINDINGS.get(cls);
獲取 Activity 類(lèi)對(duì)象名稱(chēng) , 即 " com.butterknife.demo.MainActivity " ,
String clsName = cls.getName();
得到名稱(chēng)后 , 判斷該類(lèi)對(duì)象是否是系統(tǒng)的 API , 如果是則退出 ; 如果不是 , 則繼續(xù)向下執(zhí)行 ,
if (clsName.startsWith("android.") || clsName.startsWith("java.")
|| clsName.startsWith("androidx.")) {
if (debug) Log.d(TAG, "MISS: Reached framework class. Abandoning search.");
return null;
}
拼裝要生成的類(lèi)名稱(chēng) , “com.butterknife.demo.MainActivity_ViewBinding” , 并自動(dòng)生成該類(lèi) ;
Class<?> bindingClass = cls.getClassLoader().loadClass(clsName + "_ViewBinding");
ButterKnife 涉及到的源碼 :
public final class ButterKnife {
/**
* BindView annotated fields and methods in the specified {@link Activity}. The current content
* view is used as the view root.
*
* @param target Target activity for view binding.
*/
@NonNull @UiThread
public static Unbinder bind(@NonNull Activity target) {
View sourceView = target.getWindow().getDecorView();
return bind(target, sourceView);
}
/**
* BindView annotated fields and methods in the specified {@code target} using the {@code source}
* {@link View} as the view root.
*
* @param target Target class for view binding.
* @param source View root on which IDs will be looked up.
*/
@NonNull @UiThread
public static Unbinder bind(@NonNull Object target, @NonNull View source) {
Class<?> targetClass = target.getClass();
if (debug) Log.d(TAG, "Looking up binding for " + targetClass.getName());
Constructor<? extends Unbinder> constructor = findBindingConstructorForClass(targetClass);
if (constructor == null) {
return Unbinder.EMPTY;
}
//noinspection TryWithIdenticalCatches Resolves to API 19+ only type.
try {
return constructor.newInstance(target, source);
} catch (IllegalAccessException e) {
throw new RuntimeException("Unable to invoke " + constructor, e);
} catch (InstantiationException e) {
throw new RuntimeException("Unable to invoke " + constructor, e);
} catch (InvocationTargetException e) {
Throwable cause = e.getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
}
if (cause instanceof Error) {
throw (Error) cause;
}
throw new RuntimeException("Unable to create binding instance.", cause);
}
}
@Nullable @CheckResult @UiThread
private static Constructor<? extends Unbinder> findBindingConstructorForClass(Class<?> cls) {
Constructor<? extends Unbinder> bindingCtor = BINDINGS.get(cls);
if (bindingCtor != null || BINDINGS.containsKey(cls)) {
if (debug) Log.d(TAG, "HIT: Cached in binding map.");
return bindingCtor;
}
String clsName = cls.getName();
if (clsName.startsWith("android.") || clsName.startsWith("java.")
|| clsName.startsWith("androidx.")) {
if (debug) Log.d(TAG, "MISS: Reached framework class. Abandoning search.");
return null;
}
try {
Class<?> bindingClass = cls.getClassLoader().loadClass(clsName + "_ViewBinding");
//noinspection unchecked
bindingCtor = (Constructor<? extends Unbinder>) bindingClass.getConstructor(cls, View.class);
if (debug) Log.d(TAG, "HIT: Loaded binding class and constructor.");
} catch (ClassNotFoundException e) {
if (debug) Log.d(TAG, "Not found. Trying superclass " + cls.getSuperclass().getName());
bindingCtor = findBindingConstructorForClass(cls.getSuperclass());
} catch (NoSuchMethodException e) {
throw new RuntimeException("Unable to find binding constructor for " + clsName, e);
}
BINDINGS.put(cls, bindingCtor);
return bindingCtor;
}
}
了解了butterKnife原理之后辞居,接下來(lái)字節(jié)手寫(xiě)butterknife注解實(shí)現(xiàn)楷怒。
四、創(chuàng)建 編譯時(shí)注解 和 注解處理器
4.1 使用 Android Studio 開(kāi)發(fā) Android 項(xiàng)目時(shí) , 使用到編譯時(shí)技術(shù) , 都要用到 編譯時(shí)注解 和 注解處理器 ;
編譯時(shí)注解 和 注解處理器 一般都創(chuàng)建為 Java or Kotlin Library 類(lèi)型的 Module ;
右鍵點(diǎn)擊工程名 , 選擇 " New / Module " 選項(xiàng) ,
在彈出的 " Create New Module " 對(duì)話(huà)框中 , 這里選擇 Module 的類(lèi)型為 Java or Kotlin Library ;
設(shè)置編譯時(shí)注解 依賴(lài)庫(kù)名稱(chēng) annotation , 注意語(yǔ)言選擇 Java ; 暫時(shí)不涉及 Kotlin 注解 ;
使用上述相同的方式 , 創(chuàng)建 annotation-compiler 注解處理器 依賴(lài)庫(kù) , 這兩個(gè) Module 的類(lèi)型都是 " Java or Kotlin Library " ;
4.2 接下來(lái)為項(xiàng)目app模塊添加annotation和annotation-compiler模塊的依賴(lài)
在主應(yīng)用 " app " 中 , 依賴(lài)上述 annotation 編譯時(shí)注解 依賴(lài)庫(kù) 和 annotation-compiler 注解處理器 依賴(lài)庫(kù) ;
右鍵點(diǎn)擊應(yīng)用 , 選擇 " Open Modules Settings " 選項(xiàng) ,
在 " Project Structure " 對(duì)話(huà)框中選擇 " Dependencies " 選項(xiàng)卡 , 選擇主應(yīng)用 " app " , 點(diǎn)擊 " + " 按鈕 , 選擇添加 " Module Dependency " 依賴(lài)庫(kù) ,
將 annotation 編譯時(shí)注解 依賴(lài)庫(kù) 和 annotation-compiler 注解處理器 依賴(lài)庫(kù) 添加到主應(yīng)用 " app " 的依賴(lài)中 ;
添加依賴(lài)完成 ;
點(diǎn)擊 " OK " 按鈕后 , 在 build.gradle 構(gòu)建腳本中自動(dòng)生成的依賴(lài) :
dependencies {
implementation project(path: ':annotation-compiler')
implementation project(path: ':annotation')
}
注意 : 對(duì)于 annotation-compiler 注解處理器 依賴(lài)庫(kù) 不能使用 implementation , 必須使用 annotationProcessor ,
dependencies {
annotationProcessor project(path: ':annotation-compiler')
implementation project(path: ':annotation')
}
五瓦灶、開(kāi)發(fā) annotation 編譯時(shí)注解 依賴(lài)庫(kù) ;
創(chuàng)建BindView.java注解類(lèi)
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.CLASS) //注解的生命周期鸠删,保留時(shí)間到類(lèi)文件級(jí)別
@Target(ElementType.FIELD) //作用域在字段上
public @interface BindView {
int value();
}
六、開(kāi)發(fā) annotation-compiler 注解處理器 依賴(lài)庫(kù) ;
6.1 創(chuàng)建 AnnotationCompiler 注解處理器 , 該類(lèi)主要作用是生成代碼 , 注解處理器類(lèi)必須繼承 javax.annotation.processing.AbstractProcessor 類(lèi) , 這是 Java 的 API , 再 Android 中無(wú)法獲取該 API , 因此 編譯時(shí)注解 和 注解處理器 都必須是 Java 依賴(lài)庫(kù) ;
創(chuàng)建AnnotationCompiler.java
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.element.TypeElement;
/**
* 生成代碼的注解處理器
*/
public class AnnotationCompiler extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
return false;
}
}
6.2 使用注解 @AutoService(Processor.class) 標(biāo)注 注解處理器
上述實(shí)現(xiàn)的 AbstractProcessor 中的 process 方法 , 專(zhuān)門(mén)用于搜索 Android 源碼中使用的 編譯時(shí)注解 的方法 ;
程序構(gòu)建時(shí) , 發(fā)現(xiàn)了 annotationProcessor project(path: ':annotation-compiler') 依賴(lài) , 使用 annotationProcessor 進(jìn)行依賴(lài) , 說(shuō)明這是一個(gè)注解處理器 , 此時(shí)會(huì)到 annotation-compiler 模塊中查找 注解處理器 , 注解處理器 的查找方式也是 通過(guò) 注解 標(biāo)記 進(jìn)行查找 , 該注解是 Google 服務(wù)庫(kù)提供的 ;
如果要使用 注解 標(biāo)記注解處理器 , 首先要依賴(lài) Google 服務(wù)庫(kù) ,
在 annotation-compiler模塊的build.gradle中添加
dependencies {
//使用google的auto-service依賴(lài)庫(kù)
annotationProcessor 'com.google.auto.service:auto-service:1.0-rc6'
implementation 'com.google.auto.service:auto-service:1.0-rc6'
}
然后到 public class Compiler extends AbstractProcessor 注解處理器類(lèi)上使用 @AutoService(Processor.class) 標(biāo)注 , Android 編譯器通過(guò)查找該注解 , 確定哪個(gè)類(lèi)是注解處理器 ;
import java.util.Set;
import com.google.auto.service.AutoService;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.element.TypeElement;
/**
* 生成代碼的注解處理器
*/
@AutoService(Processor.class)
public class AnnotationCompiler extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
return false;
}
}
6.3 注解處理器 init 初始化方法
AbstractProcessor 注解處理器中 , 專(zhuān)門(mén)提供了一個(gè) init 方法 , 該方法專(zhuān)門(mén)用于注解處理器相關(guān)初始化操作 ;
init 方法的 ProcessingEnvironment processingEnv 參數(shù)很重要 , 通過(guò)該參數(shù)可以獲取 注解處理器中的各種重要工具 ;
ProcessingEnvironment 中定義了獲取相關(guān)工具的接口 ;
public interface ProcessingEnvironment {
Map<String, String> getOptions();
Messager getMessager();
Filer getFiler();
Elements getElementUtils();
Types getTypeUtils();
SourceVersion getSourceVersion();
Locale getLocale();
}
6.4 注解處理器 Filer 代碼生成工具具
(1)通過(guò)注解生成 Java 代碼需要使用 Filer 對(duì)象 , 將該對(duì)象定義到 注解處理器 成員變量中 , 在 init 方法中進(jìn)行初始化操作 ;
通過(guò) ProcessingEnvironment 可以通過(guò)調(diào)用 getFiler 方法 , 獲取 Filer 對(duì)象 ;
(2)注解處理器中不能打斷點(diǎn)進(jìn)行調(diào)試 , 也不能使用 Log , System.out 打印日志 , 在注解處理器中只能通過(guò) Messager 打印日志 ;
通過(guò)調(diào)用 ProcessingEnvironment 的 getMessager 方法 , 可以獲取到 Messager 對(duì)象 ;
//生成文件的對(duì)象
private Filer filer;
//日志打印
private Messager messager;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
filer = processingEnv.getFiler();
messager = processingEnv.getMessager();
}
七倚搬、注解處理器annotation-compiler 依賴(lài) 編譯時(shí)注解annotation模塊
注解處理器 需要處理 編譯時(shí)注解 , 因此必須能夠拿到 編譯時(shí)注解 的引用 , 注解處理器 Module 需要依賴(lài) 編譯時(shí)注解 Module ;
在 注解處理器 Module 的 build.gradle 的 dependencies 依賴(lài)中添加 implementation project(path: ':annotation') 依賴(lài) ;
plugins {
id 'java-library'
}
dependencies {
//需要依賴(lài)annotation模塊冶共,獲取這個(gè)模塊的注解
implementation project(path: ':annotation')
//使用google的auto-service依賴(lài)庫(kù)
annotationProcessor 'com.google.auto.service:auto-service:1.0-rc6'
implementation 'com.google.auto.service:auto-service:1.0-rc6'
}
java {
sourceCompatibility = JavaVersion.VERSION_1_7
targetCompatibility = JavaVersion.VERSION_1_7
}
八乾蛤、設(shè)置 注解處理器 支持的注解類(lèi)型
注解處理器 抽象類(lèi) AbstractProcessor 中的 getSupportedAnnotationTypes 方法 , 用于聲明 注解處理器 要處理的注解類(lèi)型 ;
該方法的返回值是 Set<String> , 因此可以設(shè)置多個(gè)處理的 注解類(lèi)型 ;
在 getSupportedAnnotationTypes 方法中構(gòu)造一個(gè) Set<String> 集合 , 向其中放置要解析注解的全類(lèi)名字符串 ;
/**
* 聲明要處理的注解有哪些
* @return
*/
@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> types = new HashSet<>();
types.add(BindView.class.getCanonicalName());
types.add(OnClick.class.getCanonicalName());
return types;
}
設(shè)置 注解處理器 支持的注解類(lèi)型 , 也可以使用 注解 的方式進(jìn)行聲明 ;
使用 @SupportedAnnotationTypes 注解 , 也可以聲明 注解處理器 支持的注解類(lèi)型 ;
@Documented
@Target(TYPE)
@Retention(RUNTIME)
public @interface SupportedAnnotationTypes {
/**
* Returns the names of the supported annotation types.
* @return the names of the supported annotation types
*/
String [] value();
}
注意 : 兩種方式二選一 , 不能同時(shí)存在 ;
九每界、設(shè)置 注解處理器 支持的 Java 版本
注解處理器 抽象類(lèi) AbstractProcessor 中的 getSupportedSourceVersion 方法 , 用于聲明 該注解處理器 支持的 Java 版本 ;
一般情況下要支持到最新的 Java 版本 ,
通過(guò)調(diào)用 ProcessingEnvironment 類(lèi)的 getSourceVersion 方法 , 可以獲取最新的 Java 版本 ;
/**
* 聲明支持的java版本
* @return
*/
@Override
public SourceVersion getSupportedSourceVersion() {
return processingEnv.getSourceVersion();
}
設(shè)置 注解處理器 支持的 Java 語(yǔ)言版本 , 也可以使用 注解 的方式進(jìn)行聲明 ;
使用 @SupportedSourceVersion 注解 , 也可以聲明 注解處理器 支持的 Java 語(yǔ)言版本 ;
@Documented
@Target(TYPE)
@Retention(RUNTIME)
public @interface SupportedSourceVersion {
/**
* Returns the latest supported source version.
* @return the latest supported source version
*/
SourceVersion value();
}
注意 : 兩種方式二選一 , 不能同時(shí)存在 ;
十捅僵、獲取被 注解 標(biāo)注的節(jié)點(diǎn)
處理注解的核心邏輯在 AbstractProcessor 中的 process 方法中實(shí)現(xiàn) ;
先獲取被注解標(biāo)注的節(jié)點(diǎn) , 搜索 BindView , 調(diào)用 process 方法的 RoundEnvironment roundEnvironment 參數(shù)的 getElementsAnnotatedWith 方法 , 即可搜索到整個(gè) Module 中所有使用了 BindView 注解的元素 ;
Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(BindView.class);
將 BindView 注解放在什么元素上 , 得到的就是相應(yīng)類(lèi)型的元素 , 根據(jù) 注解類(lèi)型 獲取 被該注解類(lèi)型 標(biāo)注的元素 , 元素可能是類(lèi) , 方法 , 字段 ;
在 app 模塊中 , 只有 MainActivity 中的一個(gè) 屬性字段 使用 BindView 注解 , 調(diào)用 roundEnv.getElementsAnnotatedWith(BindView.class) 方法獲取的元素就是 MainActivity 中的所有標(biāo)記@BindView的成員變量 ;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ImageView;
import android.widget.TextView;
import com.aapl.annotation.BindView;
public class MainActivity extends AppCompatActivity {
@BindView(R.id.tv1)
TextView tv1;
@BindView(R.id.tv2)
TextView tv2;
@BindView(R.id.tv3)
TextView tv3;
@BindView(R.id.iv1)
ImageView iv1;
@BindView(R.id.iv2)
ImageView iv2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
tv1.setText("bind view success");
}
}
假設(shè)在 若干 Activity 中 , 若干位置 , 使用了 BindView 注解 , 那么在獲取的所有使用了 BindView 注解的字段 Set<? extends Element> elements 中裝載了所有的使用了該注解的字段 , 這些字段來(lái)自不同的 Activity 中 ;
這就需要將 Set<? extends Element> elements 中的 字段 按照 Activity 上下文進(jìn)行分組 , 以便生成代碼 ;
這樣每個(gè) Activity 界面都對(duì)應(yīng)若干個(gè) Set<? extends Element> elements 中的元素 ;
十一、Element 注解節(jié)點(diǎn)類(lèi)型
使用注解標(biāo)注的 Element 節(jié)點(diǎn)類(lèi)型 :
ExecutableElement : 使用注解的 方法 節(jié)點(diǎn)類(lèi)型 ;
VariableElement : 使用注解的 字段 節(jié)點(diǎn)類(lèi)型 , 類(lèi)的成員變量 ;
TypeElement : 使用注解的 類(lèi) 節(jié)點(diǎn)類(lèi)型 ;
PackageElement : 使用注解的 包 節(jié)點(diǎn)類(lèi)型 ;
上述 4個(gè)類(lèi)都是 javax.lang.model.element.Element 的子類(lèi) ;
app module下完整源碼參考如下:
ButterKnife.java代碼:
import android.app.Activity;
import android.os.IBinder;
import java.lang.reflect.Constructor;
public class ButterKnife {
/**
* 獲取到生成的XXXActivity_ViewBinder類(lèi)文件
* @param obj
*/
public static void bind(Object obj) {
Class<?> clazz = obj.getClass();
String clazzName = clazz.getName()+"_ViewBinder";
try {
//獲取到XXXActivity_ViewBinder的class眨层,并創(chuàng)建class實(shí)例對(duì)象
Class<?> binderClazz = Class.forName(clazzName);
//確定一個(gè)類(lèi)binderClass是不是繼承(或?qū)崿F(xiàn)自)IButterKnife
if (IButterKnife.class.isAssignableFrom(binderClazz)) {
IButterKnife butterKnife = (IButterKnife) binderClazz.newInstance();
butterKnife.bind(obj);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
IButterKnife.java代碼:
public interface IButterKnife<T> {
void bind(T target);
}
AnnotationCompiler.java代碼:
import com.aapl.annotation.BindView;
import com.aapl.annotation.OnClick;
import com.google.auto.service.AutoService;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
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.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;
/**
* 注解處理器(目的是生成一個(gè)java文件庙楚,生成一個(gè)窗體的findViewById和onClick的代碼)
* 1.繼承AbstractProcessor
* 2.注冊(cè)服務(wù),注冊(cè)注解處理器
*/
@AutoService(Processor.class)
public class AnnotationCompiler extends AbstractProcessor {
//生成文件的對(duì)象
private Filer filer;
//日志打印
private Messager messager;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
filer = processingEnv.getFiler();
messager = processingEnv.getMessager();
}
/**
* 聲明要處理的注解有哪些
* @return
*/
@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> types = new HashSet<>();
types.add(BindView.class.getCanonicalName());
types.add(OnClick.class.getCanonicalName());
return types;
}
/**
* 聲明支持的java版本
* @return
*/
@Override
public SourceVersion getSupportedSourceVersion() {
return processingEnv.getSourceVersion();
}
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
// PackageElement 包節(jié)點(diǎn)
// TypeElement 類(lèi)節(jié)點(diǎn)
// ExecutableElement 方法節(jié)點(diǎn)
// VariableElement 成員變量節(jié)點(diǎn)
/**
* 搜索 BindView , 將 BindView 注解放在什么元素上 , 得到的就是相應(yīng)類(lèi)型的元素
* 根據(jù) 注解類(lèi)型 獲取 被該注解類(lèi)型 標(biāo)注的元素 , 元素可能是類(lèi) , 方法 , 字段 ;
* 通過(guò) getElementsAnnotatedWith 方法可以搜索到整個(gè) Module 中所有使用了 BindView 注解的元素
*/
Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(BindView.class);
/**
* 定義一個(gè)map集合,用來(lái)存儲(chǔ)所有activity中所有的變量注解集合
* key->activityName activity名字
* value->List<VariableElement> 成員變量注解集合
*/
Map<String, List<VariableElement>> map = new HashMap<>();
for (Element element : elements) {
//轉(zhuǎn)化為成員變量節(jié)點(diǎn)
VariableElement variableElement = (VariableElement) element;
/**
* 先獲取該注解節(jié)點(diǎn)的上一級(jí)節(jié)點(diǎn) , 注解節(jié)點(diǎn)是 VariableElement 成員字段節(jié)點(diǎn),
* 上一級(jí)節(jié)點(diǎn)是就是 Activity 類(lèi)節(jié)點(diǎn)對(duì)應(yīng)的 類(lèi)節(jié)點(diǎn) TypeElement
*/
TypeElement typeElement = (TypeElement) variableElement.getEnclosingElement();
String qualifiedName = typeElement.getQualifiedName().toString();
//獲取到activityName = simpleName
String simpleName = typeElement.getSimpleName().toString();
System.out.println("qualifiedName="+qualifiedName+", simpleName="+simpleName);
messager.printMessage(Diagnostic.Kind.NOTE, "process==>qualifiedName="+qualifiedName+", simpleName="+simpleName);
//把當(dāng)前activity中獲取到的所有變量注解的節(jié)點(diǎn)都加入到list集合中
List<VariableElement> variableElements = map.get(simpleName);
if (variableElements == null) {
variableElements = new ArrayList<>();
map.put(simpleName, variableElements);
}
variableElements.add(variableElement);
}
if (!map.isEmpty()) {
Writer writer = null;
Iterator iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, List<VariableElement>> entry = (Map.Entry<String, List<VariableElement>>) iterator.next();
String activityName = entry.getKey();
List<VariableElement> variableElements = entry.getValue();
//獲取包名
String packageName = "";
TypeElement typeElement = null;
if (variableElements != null && !variableElements.isEmpty()) {
packageName = getPackageName(variableElements.get(0));
typeElement = (TypeElement) variableElements.get(0).getEnclosingElement();
}
// 生成一個(gè)java文件
String bindActivityName = activityName+"_ViewBinder";
try {
JavaFileObject javaFileObject = filer.createSourceFile(
packageName+"."+bindActivityName, typeElement);
writer = javaFileObject.openWriter();
StringBuffer sb = new StringBuffer();
sb.append("package "+packageName+";\n\n");
sb.append("import android.view.View;\n\n");
sb.append("public class "+bindActivityName+" implements IButterKnife<"+packageName+"."+activityName+"> {\n\n");
sb.append(" public void bind("+packageName+"." + activityName + " target) {\n");
for (VariableElement variableElement : variableElements) {
//獲取到成員變量的名字
String filedName = variableElement.getSimpleName().toString();
//獲取到注解中的值,即(R.id.xxx)
int resId = variableElement.getAnnotation(BindView.class).value();
sb.append(" target."+filedName+" = target.findViewById("+resId+");\n");
}
sb.append("");
sb.append(" }\n");
sb.append("}\n");
writer.write(sb.toString());
} catch (Exception e) {
e.printStackTrace();
} finally {
if (writer != null) {
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
return false;
}
/**
* 獲取包名
* @param typeElement
* @return
*/
public String getPackageName(Element typeElement) {
PackageElement packageElement = processingEnv.getElementUtils().getPackageOf(typeElement);
String qualifiedName = packageElement.getQualifiedName().toString();
return qualifiedName;
}
}
MainActivity.java代碼:
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ImageView;
import android.widget.TextView;
import com.aapl.annotation.BindView;
public class MainActivity extends AppCompatActivity {
@BindView(R.id.tv1)
TextView tv1;
@BindView(R.id.tv2)
TextView tv2;
@BindView(R.id.tv3)
TextView tv3;
@BindView(R.id.iv1)
ImageView iv1;
@BindView(R.id.iv2)
ImageView iv2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
tv1.setText("bind view success");
}
}
運(yùn)行之后生成的MainActivity_ViewBinder.java
import android.view.View;
public class MainActivity_ViewBinder implements IButterKnife<com.aapl.butterknifedemo.MainActivity> {
public void bind(com.aapl.butterknifedemo.MainActivity target) {
target.tv1 = target.findViewById(2131231129);
target.tv2 = target.findViewById(2131231130);
target.tv3 = target.findViewById(2131231131);
target.iv1 = target.findViewById(2131230915);
target.iv2 = target.findViewById(2131230916);
}
}