ButterKnife原始碼解析

weixin_34148456發表於2018-05-29

ButterKnife(https://github.com/JakeWharton/butterknife)是一款android平臺的依賴注入框架,通過該工具可以實現View、OnClickListener的注入,省去了findViewById、setOnClickListener的過程。使用方法如下:

5328002-958afb493caa6620.png

通過@BindView註解實現findViewById的功能,完成View注入;通過@OnClick完成OnClickListener點選事件的注入,給ID對應的View設定點選事件和響應函式。關於註解的定義和解析可以參照這篇文章:Java註解。ButterKnife使用的就是編譯時解析註解的技術,在編譯時對註解進行解析,生成中間檔案,在ButterKnife.bind時引用註解編譯器生成的中間檔案,完成依賴注入。

註解的定義

5328002-348dd576219d3a44.png
BindView註解定義

BindView註解定義中使用了元註解@Retention(CLASS)定義了該註解只保留到編譯期間,執行時會丟棄;@Target(FIELD)表示該註解只能用在成員變數上面。

5328002-05cb5b1390314ee3.png
OnClick註解

OnClick註解中@Target(METHOD)表示該註解只能用於方法上;

5328002-5ec441d686e9530d.png
ListenerClass

ListenerClass是一個@Target(ANNOTATION_TYPE)型別的註解,表示ListenerClass只能用在註解上;且@Retention(RUNTIME)表示該註解可以保留到JVM中,也就是執行時能夠通過反射來獲取。

註解的解析

下面對@BindView和@OnClick兩種註解的解析進行講解。編譯時註解的解析:

 編譯時 Annotation 指 @Retention 為 CLASS 的 Annotation,由編譯器自動解析。需要做的

    a. 自定義類整合自 AbstractProcessor(編譯器在編譯時自動查詢所有繼承自 AbstractProcessor 的類,然後呼叫他們的 process 方法去處理)

    b. 重寫其中的 process 函式

ButterKnife實現了ButterKnifeProcessor來進行編譯時註解的解析:

5328002-7b2f489b827e7ef5.png
ButterKnifeProcessor

ButterKnifeProcessor.process()函式如下:

5328002-6d72c257641ce828.png
ButterKnifeProcessor.process

process函式先呼叫findAndParseTargets生成bindingMap,然後通過binding.brewJava老生成Java檔案。findAndParseTargets的實現如下(這裡只關注@BindView和@OnClick):

5328002-69fa614836adb85b.png
findAndParseTargets

其中呼叫parseBindView對註解為@BindView的Field進行解析;findAndParseListener對@OnClick之類的Listener註解進行解析。parseBindView程式碼如下:

5328002-5bc5e2acf71bf1f7.png
parseBindView

parseBindView的主要工作是建立了BindingSet.Builder。getOrCreateBindingBuilder()如下:

5328002-a832a26d544b2754.png
getOrCreateBindingBuilder

getOrCreateBindingBuilder內部呼叫了BindingSet.newBuilder。

5328002-3027f52f426d85be.png
BindingSet.newBuilder

newBuilder生成了Builder物件,Builder物件定義了生成的Java檔名、mView所屬物件的型別等。Builder物件生產後,parseBindView就根據@BindView註解資訊生成FieldViewBinding物件,之後呼叫了Builder.build()函式;@BindView的解析已經完成後,最後通過BindingSet.brewJava來生成中間檔案。@BindView在生成檔案對應瞭如下:

5328002-2168f1f58a980b72.png
addViewBinding

生成的中間檔案如下所示:

5328002-760301bd0fc0b6ce.png
中間檔案

可以看到,中間檔案裡完成了對Target中成員變數的注入。

那麼中間檔案又是在什麼時候被呼叫的呢?答案就是ButterKnife.bind(this)

5328002-ca8a8993183953b3.png

bind函式根據呼叫的類名查詢其對應的className_ViewBinding的類名,然後反射呼叫其建構函式。

至此,ButterKnife的@BindView的執行流程就是這樣。

相關文章