capt 全稱 Class Annotation Processor Tool,是作者基於 ASM 和 Android Transform API 打造的 Android 平臺的位元組碼的註解處理工具。
起源
apt
capt 的靈感有很大一部分來自於 apt。作為一個 Android 開發者,雖然 apt 已經足夠強大,但是有個缺陷一直是我們的痛點:只支援原始碼。
Android 日常開發中,大量的依賴了第三方庫如 AAR / JAR,而他們是已經編譯成了 class 位元組碼,而 apt 僅支援原始碼級別的註解處理,因為我們不得不通過很多曲折的方式來實現原本簡單的邏輯。
舉個例子:阿里開源的 ARouter,在早期只有 apt 的版本中對模組化的處理是通過顯式的新增模組引數來實現的,在新版中也通過註冊 Android Transform API 在打包過程中修改部分位元組碼打到目的。
Lancet
Lancet 是作者在早些時候在 eleme 開源的 Android AOP 框架,就是基於 Transform API 來實現的。由於 GitHub eleme 組織的廢棄,在幾個月前作者建立了獨立的 Lancet 專案並規劃 Lancet2 的開發。在思考的過程中,覺得僅僅是 Lancet2 還不夠酷,有以下幾個原因:
- 註解是固定的幾種,使用很受限制
- 無論是 Lancet2 還是 Lancet1 都有大量重複的程式碼,浪費在主流程的無關邏輯上
於是作者萌生了一個想法,我們能不能做一個工具,只做註解處理和程式碼轉換的分發邏輯,具體的業務邏輯由外掛完成。
說幹就幹,於是 capt 誕生了。
capt
相比 apt,capt 除了提供註解處理之外,還允許多個外掛鏈式地修改每個類的位元組碼。 同時 capt 有以下幾大特性:
完全同步的 Variant
和annotationProcessor
類似, capt 會為每個SourceSet / BuildType / ProductFlavor
建立對應的Configuration
,你可以用如下的方式,根據不同的構建型別,來使用不同的 capt 外掛:
dependencies {
capt project(":xx")
capt "xx:xx:1.0"
capt files("...")
releaseCapt ...
androidTestCapt ...
}
複製程式碼
Application & Library
capt 同時支援 Android Application 和 Library 的註解處理和注入程式碼:
- Application: 所有的 runtime class
- Library: 僅該 Library 自身
APK & AndroidTest
capt 支援打普通 APK 和 AndroidTest 時注入:
- APK: 打普通 APK 時會傳遞所有的 APK 中的類
- AndroidTest: AndroidTest 打包時只會傳遞所有 androidTest 目錄下的類
靈活的引數
capt 會為每個註冊的外掛建立一個 Gradle Extension 物件,可以傳入任意形式的引數,在外掛的生命週期傳遞給外掛,同時每個 Extension 也會內建外掛的公共引數,如優先順序(可覆蓋外掛中宣告的預設優先順序)、作用域(Assemble | AndroidTest)等。
極致的增量更新
capt 會針對每個 Variant 有自身的快取,記錄類圖、外掛和外掛的修改的類等元資訊。
註解處理
capt 會解析類圖,分析類圖中類的的變化(新增、修改、刪除),因此 capt 要求所有的外掛註解處理器都要支援增量解析,因此 capt 對打包時間的影響很小。並且效能一直是 cpat 持續追求的目標
當然有得有失,因為在增量模式下,含有註解的類沒有發生變化是不會傳遞到註解處理器中的,因此需要每個外掛自己去實現本地快取的邏輯。
類轉換
capt 遵循最小原則,因此類轉換有3步:
- capt 會詢問每個外掛需要哪些類(全量、增量、無),以及是否解析額外的類
- 如果上次構建過程中的外掛被移除(移除外掛不會打斷增量構建流程,增加會),capt 會額外新增被移除外掛修改過的類
- 最後 capt 會彙總上面的資訊,並據此來分發類的轉換流程。
豐富的 API
capt 在外掛的生命週期會提供豐富的 API,如類結構圖、上下文、甚至於ClassLoader,讓每個外掛的奇思妙想都能夠實現。如果有更多更好的想法也可以提 Issue & PR 哦。
對 ASM 的高度支援
capt 允許在類轉換前對 ClassReader 和 ClassWriter 請求額外的標誌位,從而提供了最大的靈活性,同時預設都為 0 以期最佳效能。
並且 capt 為 ClassWriter.COMPUTE_FRAME 生成了獨特的 ClassLoader,開發者不再需要糾結於程式碼中的計算棧幀資訊的問題。
未來規劃
- 基於 capt 開發 Lancet2
- 持續優化效能
- 更多豐富的 API
開源地址
如果覺得 capt 還不錯,請給 capt 一個 Star! 歡迎 Issue & PR。