ButterKnife的原理簡述java
註解處理器
Java5 中叫APT(Annotation Processing Tool),在Java6開始,規範化爲 Pluggable Annotation Processing。android
找到全部被註解的屬性或者方法,將全部的信息收集到對應的「類數據集」中。緩存
根據每個「類數據集」,生成對應的java源文件。因爲這些文件並非在運行時生成的,所以也無需動態編譯,註解處理器運行完成以後,
編譯器會處理全部的編譯流程。app
運行時動態注入,即用戶常規調用的 ButterKnife.bind(activity)ide
這一步爲了不蹩腳的調用,使用了運行時反射,可是做者對每個類進行了緩存,所以,不會對執行效率產生多大影響。工具
在最新的 ButterKnife 源碼(2015.06.08)中,ButterKnife已經重構了部分方法:命令行
ButterKnife#inject -> ButterKnife#bindcode
@InjectView -> @FindView對象
等等,具體變化能夠去看官方文檔,**本文檔後續代碼使用最新版本代碼演示**。接口
極簡實現演示
示例代碼由 ButterKnife 簡化而來,部分定義和實現有刪改,只能綁定 Activity 中的 View 字段 爲了不引入Android平臺,可是又須要更直觀,因此mock了android的兩個類,Activity 和 View 爲了不使用 Pluggable Annotation Processing 過程當中的jar包要求,以及波及沒必要要的java文件,請使用命令行運行演示,直接運行 ./run.sh 便可查看結果 保證 CLASSPATH 中含有tools.jar
在每個類中找到全部被 FindView 註解的字段
每個須要綁定的字段信息都保存爲一個 FieldViewBinding 對象,好比:
@FindView(100) View vView1; 獲得: new FieldViewBinding(vView1, android.view.View, 100)
將字段分類,獲取每個類的「類數據集」BindingClass,好比, MainActivity 對應的 「類數據集」 以下:
MainActivity: List<FieldViewBinding> fieldViewBindings = new ArrayList<FieldViewBinding>(); fieldViewBindings.add(new FieldViewBinding(vView1, android.view.View, 100)) fieldViewBindings.add(new FieldViewBinding(vView2, android.view.View, 200))
爲了便於在反射時容易實例化生成的類,每個生成的類都實現了一個 ActivityBinder 接口,所以,根據 MainActivity 「類數據集」生成的文件以下:
package sample; import android.view.View; import android.app.Activity; import butterknife.ButterKnife.ActivityBinder; public class MainActivity$$ViewBinder implements ActivityBinder<sample.MainActivity> { @Override public void bind(sample.MainActivity target) { View view; view = target.findViewById(100); target.vView1 = view; //這裏要求 vView1 的訪問權限爲 package 級別 view = target.findViewById(200); target.vView2 = view; } }
咱們在 MainActivity 中調用 ButterKnife#bind,第一件事就是找到對應生成的 Bind 工具類,這裏遵循命名規則(在對應類後增長 $$ViewBinder 後綴),直接使用動態加載並實例化:
Class<?> activityBindingClass = Class.forName(targetClass.getName() + ButterKnifeProcessor.SUFFIX); activityBinder = (ActivityBinder) activityBindingClass.newInstance();
得到相應的 ActivityBinder 以後,使用 ActivityBinder#bind 進行綁定,與手動調用 findViewById 效果相同
運行:
ButterKnifeProcedure/src$ ./run.sh
結果:
mainActivity.vView1.id = 100 mainActivity.vView2.id = 200