跳至主要内容

為什麼要使用 KSP

編譯器外掛程式 (Compiler plugins) 是強大的元編程工具,可以大幅增強您編寫程式碼的方式。 編譯器外掛程式會將編譯器直接作為函式庫呼叫,以分析和編輯輸入的程式。 這些外掛程式還可以產生 各種用途的輸出。 例如,它們可以產生樣板程式碼 (boilerplate code),甚至可以為特別標記的程式元素產生完整的實作, 例如 Parcelable。 外掛程式有多種其他用途,甚至可以用於實作和微調語言中未直接提供的功能。

雖然編譯器外掛程式功能強大,但這種強大功能是有代價的。 即使要編寫最簡單的外掛程式,您也需要具備 一些編譯器背景知識,以及對特定編譯器的實作細節有一定程度的熟悉。 另一個實際問題是,外掛程式通常與特定的編譯器版本緊密相關, 這表示您可能需要在每次想要支援較新版本的編譯器時更新外掛程式。

KSP 使建立輕量級編譯器外掛程式更容易

KSP 旨在隱藏編譯器的變更,從而最大限度地減少使用它的處理器的維護工作。 KSP 的設計目的 是不與 JVM 綁定,以便將來可以更輕鬆地適應其他平台。 KSP 還旨在最大程度地縮短建置時間。 對於某些處理器,例如 Glide,與 kapt 相比,KSP 將完整編譯 時間最多縮短了 25%。

KSP 本身是作為編譯器外掛程式實作的。 Google 的 Maven 儲存庫上有預先建置的套件,您可以 下載並使用,而無需自己建置專案。

kotlinc 編譯器外掛程式的比較

kotlinc 編譯器外掛程式可以存取編譯器中的幾乎所有內容,因此具有最大的功能和靈活性。 另一方面,由於這些外掛程式可能會依賴編譯器中的任何內容,因此它們對 編譯器的變更很敏感,需要經常維護。 這些外掛程式還需要深入了解 kotlinc 的 實作,因此學習曲線可能很陡峭。

KSP 旨在透過定義完善的 API 隱藏大多數編譯器變更,但編譯器甚至 Kotlin 語言的重大變更可能仍需要向 API 使用者公開。

KSP 嘗試透過提供以簡單性換取功能的 API 來滿足常見的用例。 它的功能是 通用 kotlinc 外掛程式的嚴格子集。 例如,雖然 kotlinc 可以檢查表達式和語句,甚至可以 修改程式碼,但 KSP 無法。

雖然編寫 kotlinc 外掛程式可能很有趣,但也可能需要花費大量時間。 如果您無法 學習 kotlinc 的實作,並且不需要修改原始程式碼或讀取表達式,那麼 KSP 可能是一個不錯的選擇。

與反射 (Reflection) 的比較

KSP 的 API 看起來與 kotlin.reflect 類似。 它們之間的主要區別在於,KSP 中的類型引用 (type references) 需要 明確解析。 這是介面未共享的原因之一。

與 kapt 的比較

kapt 是一個出色的解決方案,它使大量 Java 注釋處理器 (Java annotation processors) 能夠直接用於 Kotlin 程式。 與 kapt 相比,KSP 的主要優勢是提高了建置效能、不與 JVM 綁定、更符合語言習慣的 Kotlin API 以及 理解僅限 Kotlin 的符號的能力。

為了在未修改的情況下執行 Java 注釋處理器,kapt 會將 Kotlin 程式碼編譯為 Java 存根 (stubs),這些存根會保留 Java 注釋處理器關心的資訊。 為了建立這些存根,kapt 需要解析 Kotlin 程式中的所有符號。 存根產生 (stub generation) 的成本約為完整 kotlinc 分析的 1/3,以及相同等級的 kotlinc 程式碼產生。 對於許多注釋處理器來說,這比處理器本身花費的時間要長得多。 例如,Glide 查看的類別數量非常有限,且具有預定義的注釋,並且其程式碼產生速度相當快。 幾乎所有建置開銷都位於存根產生階段。 切換到 KSP 將立即將編譯器中花費的時間減少 25%。

為了進行效能評估,我們在 KSP 中實作了 Glide簡化版本,以使其為 Tachiyomi 專案產生程式碼。 雖然該專案的 Kotlin 編譯總時間在我們的測試裝置上為 21.55 秒,但 kapt 花費了 8.67 秒來產生程式碼,而我們的 KSP 實作花費了 1.15 秒來產生程式碼。

與 kapt 不同,KSP 中的處理器不會從 Java 的角度查看輸入的程式。 該 API 對於 Kotlin 來說更自然, 尤其是對於僅限 Kotlin 的功能(例如頂層函式)。 因為 KSP 不像 kapt 那樣委託給 javac,所以它不假設特定於 JVM 的行為, 並且可以潛在地與其他平台一起使用。

限制

雖然 KSP 試圖成為大多數常見用例的簡單解決方案,但與其他外掛程式解決方案相比,它做出了一些權衡。 以下不是 KSP 的目標:

  • 檢查原始程式碼的表達式級別資訊。
  • 修改原始程式碼。
  • 100% 相容於 Java 注釋處理 API (Java Annotation Processing API)。