ButterKnife的原理簡述

 

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))

第二步(生成 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 = 100
mainActivity.vView2.id = 200
相關文章
相關標籤/搜索