capt 正式開源

蝶翼的罪發表於2019-01-03

capt 全稱 Class Annotation Processor Tool,是作者基於 ASMAndroid 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 還不夠酷,有以下幾個原因:

  1. 註解是固定的幾種,使用很受限制
  2. 無論是 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步:

  1. capt 會詢問每個外掛需要哪些類(全量、增量、無),以及是否解析額外的類
  2. 如果上次構建過程中的外掛被移除(移除外掛不會打斷增量構建流程,增加會),capt 會額外新增被移除外掛修改過的類
  3. 最後 capt 會彙總上面的資訊,並據此來分發類的轉換流程。

豐富的 API

capt 在外掛的生命週期會提供豐富的 API,如類結構圖、上下文、甚至於ClassLoader,讓每個外掛的奇思妙想都能夠實現。如果有更多更好的想法也可以提 Issue & PR 哦。

對 ASM 的高度支援

capt 允許在類轉換前對 ClassReader 和 ClassWriter 請求額外的標誌位,從而提供了最大的靈活性,同時預設都為 0 以期最佳效能。

並且 capt 為 ClassWriter.COMPUTE_FRAME 生成了獨特的 ClassLoader,開發者不再需要糾結於程式碼中的計算棧幀資訊的問題。

未來規劃

  • 基於 capt 開發 Lancet2
  • 持續優化效能
  • 更多豐富的 API

開源地址

github.com/CoffeePartn…

如果覺得 capt 還不錯,請給 capt 一個 Star! 歡迎 Issue & PR。

相關文章