Kotlin 符號處理 API
Kotlin 符號處理 (Symbol Processing) (KSP) 是一個可用於開發輕量級編譯器外掛程式 (compiler plugins) 的 API。KSP 提供一個簡化的編譯器外掛程式 API,它利用 Kotlin 的強大功能,同時將學習曲線保持在最低限度。相較於 kapt,使用 KSP 的註解處理器 (annotation processors) 執行速度可提高兩倍。
概述
KSP API 以慣用的方式處理 Kotlin 程式。KSP 理解 Kotlin 特有的功能,例如擴充函數 (extension functions)、宣告點變異 (declaration-site variance) 和局部函數 (local functions)。它還明確地建模類型,並提供基本的類型檢查,例如等價 (equivalence) 和賦值相容性 (assign-compatibility)。
該 API 根據 Kotlin 文法 在符號層級建模 Kotlin 程式結構。當基於 KSP 的外掛程式處理原始程式時,類別、類別成員、函數和相關參數等構造對於處理器而言是可存取的,而 if
區塊和 for
迴圈等則不可存取。
從概念上講,KSP 與 Kotlin 反射 (reflection) 中的 KType 相似。該 API 允許處理器從類別宣告導航到具有特定類型參數 (type arguments) 的相應類型,反之亦然。您還可以替換類型參數、指定變異 (variances)、應用星形投影 (star projections) 和標記類型的可空性 (nullabilities)。
另一種看待 KSP 的方式是將其視為 Kotlin 程式的前處理器框架 (preprocessor framework)。將基於 KSP 的外掛程式視為 符號處理器,或簡稱為 處理器,編譯中的資料流可以透過以下步驟描述:
- 處理器讀取並分析原始程式和資源。
- 處理器產生程式碼或其他形式的輸出。
- Kotlin 編譯器將原始程式與產生的程式碼一起編譯。
不同於一個功能齊全的編譯器外掛程式,處理器無法修改程式碼。改變語言語義 (semantics) 的編譯器外掛程式有時會非常令人困惑。KSP 透過將原始程式視為唯讀 (read-only) 來避免這種情況。
您也可以透過此影片瞭解 KSP 概述:
KSP 如何看待原始碼檔案
大多數處理器會遍歷輸入原始碼的各種程式結構。在深入探討 API 的使用之前,讓我們看看 KSP 如何看待一個檔案:
KSFile
packageName: KSName
fileName: String
annotations: List<KSAnnotation> (檔案註解)
declarations: List<KSDeclaration>
KSClassDeclaration // 類別、介面、物件
simpleName: KSName
qualifiedName: KSName
containingFile: String
typeParameters: KSTypeParameter
parentDeclaration: KSDeclaration
classKind: ClassKind
primaryConstructor: KSFunctionDeclaration
superTypes: List<KSTypeReference>
// 包含內部類別、成員函數、屬性等。
declarations: List<KSDeclaration>
KSFunctionDeclaration // 頂層函數
simpleName: KSName
qualifiedName: KSName
containingFile: String
typeParameters: KSTypeParameter
parentDeclaration: KSDeclaration
functionKind: FunctionKind
extensionReceiver: KSTypeReference?
returnType: KSTypeReference
parameters: List<KSValueParameter>
// 包含局部類別、局部函數、局部變數等。
declarations: List<KSDeclaration>
KSPropertyDeclaration // 全域變數
simpleName: KSName
qualifiedName: KSName
containingFile: String
typeParameters: KSTypeParameter
parentDeclaration: KSDeclaration
extensionReceiver: KSTypeReference?
type: KSTypeReference
getter: KSPropertyGetter
returnType: KSTypeReference
setter: KSPropertySetter
parameter: KSValueParameter
這種視圖列出了檔案中宣告的常見內容:類別、函數、屬性等等。
SymbolProcessorProvider:入口點
KSP 期望 SymbolProcessorProvider
介面的實作來實例化 SymbolProcessor
:
interface SymbolProcessorProvider {
fun create(environment: SymbolProcessorEnvironment): SymbolProcessor
}
而 SymbolProcessor
則定義為:
interface SymbolProcessor {
fun process(resolver: Resolver): List<KSAnnotated> // 讓我們專注於此
fun finish() {}
fun onError() {}
}
Resolver
提供 SymbolProcessor
存取編譯器詳細資訊,例如符號。尋找所有頂層函數和頂層類別中非局部函數的處理器可能看起來像以下這樣:
class HelloFunctionFinderProcessor : SymbolProcessor() {
// ...
val functions = mutableListOf<KSClassDeclaration>()
val visitor = FindFunctionsVisitor()
override fun process(resolver: Resolver) {
resolver.getAllFiles().forEach { it.accept(visitor, Unit) }
}
inner class FindFunctionsVisitor : KSVisitorVoid() {
override fun visitClassDeclaration(classDeclaration: KSClassDeclaration, data: Unit) {
classDeclaration.getDeclaredFunctions().forEach { it.accept(this, Unit) }
}
override fun visitFunctionDeclaration(function: KSFunctionDeclaration, data: Unit) {
functions.add(function)
}
override fun visitFile(file: KSFile, data: Unit) {
file.declarations.forEach { it.accept(this, Unit) }
}
}
// ...
class Provider : SymbolProcessorProvider {
override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor = TODO()
}
}
資源
支援的函式庫
下表列出了 Android 上流行的函式庫及其對 KSP 的各種支援階段:
函式庫 | 狀態 |
---|---|
Room | 官方支援 |
Moshi | 官方支援 |
RxHttp | 官方支援 |
Kotshi | 官方支援 |
Lyricist | 官方支援 |
Lich SavedState | 官方支援 |
gRPC Dekorator | 官方支援 |
EasyAdapter | 官方支援 |
Koin Annotations | 官方支援 |
Glide | 官方支援 |
Micronaut | 官方支援 |
Epoxy | 官方支援 |
Paris | 官方支援 |
Auto Dagger | 官方支援 |
SealedX | 官方支援 |
Ktorfit | 官方支援 |
Mockative | 官方支援 |
DeeplinkDispatch | 透過 airbnb/DeepLinkDispatch#323 支援 |
Dagger | Alpha |
Motif | Alpha |
Hilt | 進行中 |
Auto Factory | 尚未支援 |