capt 正式開源

capt 全稱 Class Annotation Processor Tool,是作者基于 ASMAndroid Transform API 打造的 Android 平臺的字節(jié)碼的注解處理工具。

起源

apt

capt 的靈感有很大一部分來自于 apt。作為一個 Android 開發(fā)者,雖然 apt 已經(jīng)足夠強(qiáng)大,但是有個缺陷一直是我們的痛點(diǎn):只支持源碼。

Android 日常開發(fā)中,大量的依賴了第三方庫如 AAR / JAR,而他們是已經(jīng)編譯成了 class 字節(jié)碼,而 apt 僅支持源碼級別的注解處理,因?yàn)槲覀儾坏貌煌ㄟ^很多曲折的方式來實(shí)現(xiàn)原本簡單的邏輯。

舉個例子:阿里開源的 ARouter,在早期只有 apt 的版本中對模塊化的處理是通過顯式的添加模塊參數(shù)來實(shí)現(xiàn)的,在新版中也通過注冊 Android Transform API 在打包過程中修改部分字節(jié)碼打到目的。

Lancet

Lancet 是作者在早些時候在 eleme 開源的 Android AOP 框架,就是基于 Transform API 來實(shí)現(xiàn)的。由于 GitHub eleme 組織的廢棄,在幾個月前作者創(chuàng)建了獨(dú)立的 Lancet 項(xiàng)目并規(guī)劃 Lancet2 的開發(fā)。在思考的過程中,覺得僅僅是 Lancet2 還不夠酷,有以下幾個原因:

  1. 注解是固定的幾種,使用很受限制
  2. 無論是 Lancet2 還是 Lancet1 都有大量重復(fù)的代碼,浪費(fèi)在主流程的無關(guān)邏輯上

于是作者萌生了一個想法,我們能不能做一個工具,只做注解處理和代碼轉(zhuǎn)換的分發(fā)邏輯,具體的業(yè)務(wù)邏輯由插件完成。

說干就干,于是 capt 誕生了。

capt

相比 apt,capt 除了提供注解處理之外,還允許多個插件鏈?zhǔn)降匦薷拿總€類的字節(jié)碼。
同時 capt 有以下幾大特性:

完全同步的 Variant

annotationProcessor類似, capt 會為每個SourceSet / BuildType / ProductFlavor創(chuàng)建對應(yīng)的Configuration,你可以用如下的方式,根據(jù)不同的構(gòu)建類型,來使用不同的 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 目錄下的類

靈活的參數(shù)

capt 會為每個注冊的插件創(chuàng)建一個 Gradle Extension 對象,可以傳入任意形式的參數(shù),在插件的生命周期傳遞給插件,同時每個 Extension 也會內(nèi)置插件的公共參數(shù),如優(yōu)先級(可覆蓋插件中聲明的默認(rèn)優(yōu)先級)、作用域(Assemble | AndroidTest)等。

極致的增量更新

capt 會針對每個 Variant 有自身的緩存,記錄類圖、插件和插件的修改的類等元信息。

注解處理

capt 會解析類圖,分析類圖中類的的變化(添加、修改、刪除),因此 capt 要求所有的插件注解處理器都要支持增量解析,因此 capt 對打包時間的影響很小。并且性能一直是 cpat 持續(xù)追求的目標(biāo)

當(dāng)然有得有失,因?yàn)樵谠隽磕J较拢凶⒔獾念悰]有發(fā)生變化是不會傳遞到注解處理器中的,因此需要每個插件自己去實(shí)現(xiàn)本地緩存的邏輯。

類轉(zhuǎn)換

capt 遵循最小原則,因此類轉(zhuǎn)換有3步:

  1. capt 會詢問每個插件需要哪些類(全量、增量、無),以及是否解析額外的類
  2. 如果上次構(gòu)建過程中的插件被移除(移除插件不會打斷增量構(gòu)建流程,增加會),capt 會額外添加被移除插件修改過的類
  3. 最后 capt 會匯總上面的信息,并據(jù)此來分發(fā)類的轉(zhuǎn)換流程。

豐富的 API

capt 在插件的生命周期會提供豐富的 API,如類結(jié)構(gòu)圖、上下文、甚至于ClassLoader,讓每個插件的奇思妙想都能夠?qū)崿F(xiàn)。如果有更多更好的想法也可以提 Issue & PR 哦。

對 ASM 的高度支持

capt 允許在類轉(zhuǎn)換前對 ClassReader 和 ClassWriter 請求額外的標(biāo)志位,從而提供了最大的靈活性,同時默認(rèn)都為 0 以期最佳性能。

并且 capt 為 ClassWriter.COMPUTE_FRAME 生成了獨(dú)特的 ClassLoader,開發(fā)者不再需要糾結(jié)于代碼中的計算棧幀信息的問題。

未來規(guī)劃

  • 基于 capt 開發(fā) Lancet2
  • 持續(xù)優(yōu)化性能
  • 更多豐富的 API

開源地址

https://github.com/CoffeePartner/capt

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容