背景
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…
-
保证Kotlin版本与对应ksp,及compose版本的对应关系;否则编译会出错,对应关系看前文(Releases · google/ksp,Compose 与 Kotlin 的兼容性对应关系 | Android 开发者 | Android Developers)
目前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…