
最近在研究 Java 中的 Annotation,初衷是先了解注解,然后再了解下 Android 開(kāi)發(fā)中常用框架的原理。經(jīng)歷的 Android 項(xiàng)目中,使用注解比較多的是:ButterKnife, Roboguice, Dagger 。ButterKnife 主要是 View Binding, 而 Roboguice 和 Dagger 則主要是做 DI(Dependency Injection)。本文主要研究 ButterKnife 的原理。
開(kāi)始之前,還是應(yīng)該向 ButterKnife 的作者 Jake Wharton 大神以及開(kāi)源社區(qū)致敬。
ButterKnife 初瞰
ButterKnife 到底是什么?看看官方的解釋:
Bind Android views and callbacks to fields and methods.
通過(guò) ButterKnife 官方文檔,我們可以很快速的使用它。并且可以看到 ButterKnife 主要是實(shí)例化 View 或者給某個(gè) View 添加各種事件 Listenters。

所以使用 ButterKnife 主要有幾步:
1.Gradle 中引入 ButterKnife 框架:
compile 'com.jakewharton:butterknife:8.0.1'
2.ButterKnife 初始化:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getLayoutResourceId());
ButterKnife.bind(this);
}}
3.聲明 Field/Method
@Bind(R.id.btn_home)ImageView homeBtn;
@Bind(R.id.btn_explore)ImageView exploreBtn;
@OnClick(R.id.btn_home) void clickHomeBtn() {
// Handle the home btn click
}
Java Annotation
如果你對(duì)注解了解比較少,在進(jìn)一步了解 ButterKnife 之前,有必要了解一下 Java Annotation 的基礎(chǔ)知識(shí)。可以參考如下兩份資料:
- Mkyong Java Custom Annotations
- Trenia Java Annotations
通過(guò)上面的例子以及講解,我們可以知道如何自定義注解。自定義注解我們需要知道兩個(gè)基礎(chǔ)的元注解@Retention 和 @Target.
元注解 @Retention
@Retention 保留時(shí)間:有三類值可以選擇,SOURCE RUNTIME CLASS。
SOURCE 源碼時(shí)保留:使用此類的注解多為標(biāo)記注解,比如 @Override、@Deprecated、@SuppressWarnings 等。
RUNTIME 運(yùn)行時(shí)保留:程序在運(yùn)行過(guò)程中,使用這些 Annotation, 比如我們常用的 @Test。
CLASS 編譯時(shí)保留:Java 文件在編譯時(shí)由 apt 自動(dòng)解析,需要自定義類繼承自 AbstractProcessor 并重寫 Process 函數(shù)。比如 ButterKnife 中使用的 @BindView, @OnClick 等就是聲明為 CLASS 的。
元注解 @Target
Target 表示注解可以用來(lái)修飾哪些元素。可選值包括 TYPE, METHOD, CONSTRUCTOR, FIELD, PARAMETER 等。
ButterKnifeProcessor
ButterKnife 中所有的注解都使用 Retention 為 CLASS 保留。所以在 ButterKnife 中,有個(gè)很重要的 ButterKnifeProcessor。當(dāng) java 文件進(jìn)行編譯時(shí),ButterKnifeProcessor 的 process() 方法被調(diào)用,生成相關(guān)的 ViewBinder 類,用于將 View 或者 Listener 進(jìn)行綁定。
ButterKnifeProcessor 的關(guān)鍵邏輯如下:

process() 會(huì)找到所有 ButterKnife 相關(guān)的 annotation,并將這些相應(yīng)的注解生成為 ViewBinder 類,一個(gè) ViewBinder 示例如下:

ViewBinder 通過(guò) findViewById 實(shí)例化 Activity 中頁(yè)面上的組件,并且能夠設(shè)置各控件的點(diǎn)擊時(shí)間,滑動(dòng)事件等。
為什么 ButterKnife Annotation 的字段或者方法不能聲明為 private?
bind() 方法中,通過(guò) activity.homeBtn 對(duì) View 賦值。因此所有 ButterKnife annotation 標(biāo)記的 field 或者 method 都不能聲明為 private, 因?yàn)?private 沒(méi)辦法在 ViewBinder 中直接訪問(wèn)。由此可見(jiàn):
ButterKnife 并不是依靠反射實(shí)現(xiàn) View Injection 的!
ButterKnife.bind(this)
在 ButterKnife 工作之前,我們一定得在設(shè)置 View 后調(diào)用 ButterKnife.bind(Activity) ,這樣才能使 ButterKnife View 注入成功。那么 ButterKnife.bind(Activity) 是如何工作的呢?

App 在打包時(shí),ButterKnife Processor 就為所有使用 ButterKnife 注解的 Activity 生成 ViewBinder()。通過(guò)上述代碼可以看到,ButterKnife 會(huì)嘗試實(shí)例化當(dāng)前 Activity 所關(guān)聯(lián)的所有 ViewBinder 類。實(shí)例化之后再調(diào)用 ViewBinder.bind(...) 方法,bind() 方法是在 App 運(yùn)行過(guò)程中才會(huì)被調(diào)用的。于是當(dāng)前 Avtivity 的 View 被實(shí)例化賦值,如果有點(diǎn)擊事件的注解,也會(huì)綁定相應(yīng)的事件。
結(jié)語(yǔ)
ButterKnife 主要是做 View 注入的,使用起來(lái)比較簡(jiǎn)便。當(dāng)然如果你想做一些 依賴注入,比如 Android MVP 架構(gòu)中 Activity 與 Presenter 的依賴。你可能需要借助一些其他的庫(kù), 比如 Dagger。研究下 ButterKnife 的原理,才發(fā)現(xiàn)這個(gè)庫(kù)做得如此的睿智。