當前位置:
首頁 > 最新 > 自定義Android註解Part2:代碼自動生成

自定義Android註解Part2:代碼自動生成

上一期我們已經把butterknife-annotations中的註解變數都已經定義好了,分別為BindView、OnClick與Keep。

如果你是第一次進入本系列文章,強烈推薦跳到文章末尾查看上篇文章,要不然你可能會有點雲里霧裡。

如果在代碼中引用的話,它將與開源庫ButterKnife的操作類似。

使用@BindView來綁定我的View,使用@OnClick來綁定View的點擊事件。使用Butterknife.bind來綁定該Class,主要是用來實例化自動生成的類。(該部分下篇文章將提及)

我們自己定義的綁定註解庫已經完成了1/3,接下來我們將實現它的代碼自動生成部分。這時就到了上期提到的第二個Module:butterknife-compiler。

NameUtils是一些常量的管理工具類。

NameUitls包含了自動生成的類名稱,包名,方法名,變數名。總之就是為了代碼更健全,方便管理。

第二個類Processor是今天的重中之重。也是註解庫代碼自動生成的核心部分。由於註解的自動生成代碼都是在註解進程中進行,所以這裡它繼承於AbstractProcessor,其中主要有三個方法需要實現。

init:初始化必要的數據

getSupportedAnnotationTypes:所支持的註解

process:解析註解,編寫自動生成代碼


從簡單到容易,先是init方法,我們直接看代碼

方法參數processingEnv為我們提供註解處理所需的環境狀態。我們通過getFiler()、getMessager()與getElementUthis()方法,分別獲取創建源代碼的Filer、消息發送器Messager(主要用於向外界發送錯誤信息)與解析註解元素所需的通用方法。

例如:當我們已經構建好了需要自動生成的類,這時我們就可以使用Filter來將代碼寫入到java文件中,如遇錯誤使用Messager將錯誤信息發送出去。

代碼中的JavaFile與typeBuilder都是JavaPoet中的類。JavaPote主要提供Java API來幫助生成資源文件。


看方法名就知道了,包含所支持的註解,將其通過set集合來返回。這裡將我們上一期自定義的註解添加到set集合中即可。


到了本篇文章的核心,process用來生成與註解相匹配的方法代碼。通過解析Class中定義的註解,生成與註解相關聯的類。

提供了兩個參數:annotations與roundEnv,分別代表需要處理的註解,這裡就代表我們自定義的註解;註解處理器所需的環境,幫助進行解析註解。

在開始解析註解之前,我們應該先過濾我們所不需要的註解。回頭看getSupportedAnnotationTypes方法,我們只支持BindView、OnClick與Keep這三個註解。為了解析出相匹配的註解,我們將這個邏輯單獨抽離出來,交由getTypeElementsByAnnotationType來管理。

首先理解Element是什麼?Element代表程序中的包名、類、方法,這也是註解所支持的作用類型。然後再回到代碼部分,已經給出詳細代碼注釋。

該方法的作用就是獲取到有我們自定義註解的class。這裡介紹兩個主要的方法

getEnclosedElements():獲元素中的閉包的註解元素,在我們的實例中元素為MainActivity(TypeElement,Type代表Class),而閉包的註解元素則為sName、sPhone、nameClick、phoneClick與onCreate。在這裡簡單的理解就是獲取有註解的欄位名、方法名

getAnnotationMirrors():獲取上述閉包元素的所有註解。這裡分別為sName與sPhone上的@BindeView、nameClick與phoneClick上的@OnClick、onCreate上的@Override。

所以通過該方法最終返回的就是MainActivity,它將被轉化為TypeElement類型返回,然後將由processing來處理。

我們再回到process方法中。通過getTypeElementsByAnnotationType()方法我們已經獲取到了我們使用了自定義註解的TypeElement(MainActivity)。

下面我們再獲取構建類所需的相關信息。

注釋已經劃分清楚了,可以分為四個步驟

所有信息準備完畢後,然後開始定義自動生成的類。這裡通過使用TypeSpec.Builder來構建。它是JavaPoet中的類。


由於直接使用JavaFileObject生成.java資源文件是非常麻煩的,所以推薦使用JavaPoet。JavaPoet是一個開源庫,主要用來幫助方便快捷的生成.java的資源文件。想要全面了解的可以查看

Githubhttps://github.com/square/javapoet

為了幫助快速讀懂該文章,這裡對其中幾個主要方法進行介紹。當然在使用前還需在butterknife-compiler中的builder.gradle添加依賴:

同時也將上一期我們自定義的註解Module引入。

TypeSpec.Builder: 定義一個類

addModifiers: 定義private、public與protected類型

addAnnotation: 對Element元素添加註解。例如:@Keep

TypeSpec.Builder -> addMethod: 添加方法

MethodSpec -> addParameter: 為方法添加參數類型與參數名

MethodSpec -> addStatement: 在方法中添加代碼塊。而其中的一些動態類型會使用佔位符替代。例如:addStatement("$N($N)", "bindView", "activity"),它將會生成bindView(activity)。佔位符:$N -> name, $T -> type(ClassName), $L -> literals

有了上面的理解我們再來看下面的生成代碼:

首先通過TypeSpec.Builder構建一個類,類名為autoGenerationClassName(MainActivity$Binding),類的訪問級別為public,由於為了防止混淆使用了我們自定義的@Keep註解。

然後再來添加類的構造方法,使用addMethod、addModifiers、addParameter與addStatement分別構建構造方法名、方法訪問級別、方法參數與方法中執行的代碼塊。所以上面的代碼最終將會自動生成如下代碼:

在自動生成類的構造方法中調用了我們想要的bindView與setOnClickListener方法。所以接下來我們要實現的就是這兩個方法的構建。


使用MethodSpec.Builder來創建bindView方法,其它的都與構造方法類似。使用returns為方法返回void類型。然後再遍歷MainActivity中的註解,找到與我們定義的BindView相匹配的欄位。最後分別向bindView方法中添加findViewById與setText代碼塊,同時將定義的方法添加到typeBuilder中。所以執行完上面代碼後在MainActivity$Binding中展示如下:

實現了我們最初的View的綁定與TextView的默認值設置。


與bindView方法不同的是,由於使用到了匿名類OnClickListener與類View,所以我們這裡也要定義他們的ClassName,然後使用TypeSpec來生成匿名類。生成之後再添加到setOnClickListener方法中。最後再將setOnClickListener方法添加到MainActivity$Binding中。所以最終展示如下:

我們的MainActivity$Binding類就已經定義完成,最後再寫入到java文件中


在butterknife-compiler中,我們還需創建一個特定的目錄:

在services目錄中,我們還需創建一個文件:,該文件是用來告訴編譯器,當它在編譯代碼的過程中正處於註解處理中時,會告訴註解處理器來自動生成哪些類。

所以我們在文件中將添加我們自定義的Processor路徑

這樣註解器就會調用該指定的Processor。到這裡整個butterknife-compiler就完成了,現在我們可以一下工程,完成之後就可以全局搜索到MainActivity$Binding文件了。或者在如下路徑中查看:

Github:https://github.com/idisfkj/android-api-analysis

使用時請將分支切換到feat_annotation_processing


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 怪談時間到了 的精彩文章:

TAG:怪談時間到了 |