kapt 迁移 KSP 指南

1,001 阅读4分钟

背景

KSP的推出主要是为了解决Kotlin 没有原生注释处理系统的问题,之前依赖 Kotlin 注释处理工具 (KAPT) 实现的Java 注释处理兼容性。但是,KAPT 的运行速度可能会很慢,因为它需要生成中间的 Java 存根,然后 Java 注释处理系统才能对其进行提取。

KSP 提供了一个功能强大且简单的API,它可以直接解析 Kotlin 代码,因此大大降低了 KAPT 生成存根所带来的构建速度负担。利用 Room 库执行的初始基准测试表明,KSP 相比 KAPT 速度提高了 2 倍左右。具体参考 kotlinlang.org/docs/ksp-ov…

版本依赖

KSP 兼容 Kotlin 1.4.30 及更高版本;koltin与KSP的兼容关系概括来说:

Kotlin 1.4.30 ksp进入alpha

Kotlin 1.5.10 ksp进入beta

Kotlin 1.5.30 ksp进入1.0.0正式版

详细情况参见 Releases · google/ksp

到了Kotlin 1.9.20 版本,相关编译器及稳定性已经比较可靠了,最终将在Kotlin2.0进入稳定状态;请参见K2 编译器将在 Kotlin 2.0 中进入稳定状态 | The Kotlin Blog

Compose迁移(项目中未使用Compose可以跳过)

compose依赖kotlin编译,因此kotlin版本的变更会导致compose版本的升级;具体的对应关系,参见Compose 与 Kotlin 的兼容性对应关系  |  Android 开发者  |  Android Developers

每个版本的compose都进行了Bug Fix及优化,建议保持更新;每个版本的变化,具体参见Compose Foundation  |  Jetpack  |  Android Developers

部分开发者使用了Bom对项目版本进行了管理,Bom与compose版本的对应关系,参见BOM to library version mapping  |  Jetpack Compose  |  Android Developers

Compose版本的升级过程中部分Api会发生变更,先确保相关Api变化调整到位,保证编译通过;

KSP迁移

具体的迁移步骤官方文档(developer.android.google.cn/build/migra…

目前KSP已经支持的库,参考kotlinlang.org/docs/ksp-ov…, ARouter, Room;其中Hilt及Room是官方库,ARouter是国内应用广泛的路由库;下面分别给出详细的升级步骤

Hilt升级

目前Dagger KSP处于Alpha阶段,可能会有潜在的问题,在使用时请注意测试;

Hilt升级需求:

  • Kotlin 1.9.0+
  • Dagger 2.48+
  • KSP 1.9+

建议升级到kotlin 升级到1.9.20,hilt 升级到 2.50 配置如下;

在项目build.gradle中配置依赖

id("com.google.dagger.hilt.android") version "2.50" apply false

id("com.google.devtools.ksp") version "1.9.20-1.0.13" apply false

在具体模块中,如app模块中引入ksp,应去除kapt。

ksp "com.google.dagger:hilt-compiler:2.50"

具体见developer.android.google.cn/build/migra…

  注意事项

  在Hilt处理的对象中,可能存在部分类有通配符的情况,类似abstract class BaseNewStatisticsActivity; 这种情况下默认会出现编译无法通过的情况,原因是因为ViewBinding生成的类信息对KSP不可见导致的,使用以下代码进行修复;具体原因参考 issuetracker.google.com/301245705

  build.gradle

androidComponents {
onVariants(selector().all(), { variant ->
afterEvaluate {
// This is a workaround for https://issuetracker.google.com/301245705 which depends on internal
      // implementations of the android gradle plugin and the ksp gradle plugin which might change in the future
      // in an unpredictable way.
      def dataBindingTask = project.tasks.named("dataBindingGenBaseClasses" variant.name.capitalize()).get()

      if (dataBindingTask != null) {
        project.tasks.getByName("ksp" variant.name.capitalize() + "Kotlin") {
it.setSource(dataBindingTask.sourceOutFolder)
        }
}
    }
} )
} 

  build.gradle.kts

androidComponents {
onVariants(selector().all()) { variant ->
afterEvaluate {
val dataBindingTask =
                project.tasks.findByName("dataBindingGenBaseClasses" variant.name.capitalized()) as? DataBindingGenBaseClassesTask
            if (dataBindingTask != null) {
                project.tasks.getByName("ksp" variant.name.capitalized() + "Kotlin") {
(this as AbstractKotlinCompileTool<*>).setSource(dataBindingTask.sourceOutFolder)
                }
}
        }
}
} 

ARouter 插件升级

伴随着Hilt升级到KSP,对于项目文件中使用了Hilt注解的类,生成后的类文件结构发生了变化;原来的Arouter Compiler已经无法处理了,会曝出“no module name..."错误提示,本质上是注解处理器未适配导致的;

处理方式,在模块中声明:

ksp { ****arg("AROUTER_MODULE_NAME", project.getName()) }

注解处理器替换成KSP版本的处理器

ksp 'com.github.JailedBird:ArouterKspCompiler:1.9.10-1.0.7'

插件详情,请查看GitHub - JailedBird/ArouterKspCompiler: Arouter KSP annotation processor

Room升级

Room从2.3.0版本开始支持KSP,后续版本逐渐稳定,建议升级到2.6.0及以后

   ksp("androidx.room:room-compiler:2.6.0")

  注意事项- 对于@Entity 注解的Java数据库表,如果没有@Nullable/@NonNullable注解,KSP会认为字段非空;这样可能会导致数据类型和数据库类型不一致,从而导致潜在的数据库数据保持异常;建议将所有Java数据类,转换成Kotlin类;字段是否可空,按照Room scheme中给定的数据库表进行转换;
  • 转换前的Java数据库表中,如果存在字段类型和其Get方法数据类型不统一的,也会导致潜在数据保存异常;如果需要保留相应的方法,建议修改方法名,不要导致混淆;

  • 在Room 2.6.0版本实际转换中发现,对于"is"开头的Boolean类型字段,KSP默认的查找的方法名是"setXXX"而kotlin默认提供的方法名是"isXXX"。这样会导致KSP报错,解决方法是去除字段名的"is"前缀;由于字段的更改导致了,数据库的指纹(HASH)发生了变化,因此需要对数据库版本进行升级;

Room具体版本详情,请参考developer.android.google.cn/jetpack/and…