標籤:
ButterKnifeProcedure
ButterKnife的原理簡述
範例程式碼ButterKnifeProcedure
Pluggable Annotation Processing
註解處理器
Java5 中叫APT(Annotation Processing Tool),在Java6開始,正常化為 Pluggable Annotation Processing。
第一步(收集資訊)
找到所有被註解的屬性或者方法,將所有的資訊收集到對應的“類資料集”中。
第二步(產生源檔案)
根據每一個“類資料集”,產生對應的java源檔案。由於這些檔案並不是在運行時產生的,因此也無需動態編譯,註解處理器運行完成之後,
編譯器會處理所有的編譯流程。
第三步(動態注入)
運行時動態注入,即使用者常規調用的 ButterKnife.bind(activity)
這一步為了避免蹩腳的調用,使用了運行時反射,但是作者對每一個類進行了緩衝,因此,不會對執行效率產生多大影響。
注
在最新的 ButterKnife 源碼(2015.06.08)中,ButterKnife已經重構了部分方法:
ButterKnife#inject -> ButterKnife#bind
@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))
第二步(產生 Bind 工具類源檔案)
為了便於在反射時容易執行個體化產生的類,每一個產生的類都實現了一個 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 = 100mainActivity.vView2.id = 200
Android分享 Q群:315658668
【Android】ButterKnife 簡單原理實現