2. 项目依赖与 Gradle 配置:KSP 与 KAPT 选择、room_version 定义、编译器选项、数据库版本声明

2.1 依赖注入方式:KSP 与 KAPT 的区别与选择

Room 使用注解处理器在编译阶段生成数据库访问代码。Android 提供两种注解处理机制:KAPT(Kotlin Annotation Processing Tool)和 KSP(Kotlin Symbol Processing)。KSP 是较新的选择,性能更优且与 Kotlin 编译模型更紧密。

对比维度KAPTKSP
编译速度较慢(在 Kotlin 编译器之前运行)更快(直接操作 Kotlin 符号树)
增量编译部分支持良好支持
Room 兼容性完全兼容(早期版本默认)Room 2.4.0+ 官方推荐
多模块项目可能卡顿更流畅
迁移成本无需额外配置需替换 gradle 插件和依赖
建议:新项目优先使用 KSP。如果现有项目使用 KAPT 且 Room 版本低于 2.4.0,可先升级 Room 再迁移。KSP 带来的构建时间缩短(通常 30%~50%)在大型项目中收益显著。

2.2 Gradle 依赖配置块

在模块级 build.gradle.kts(或 .gradle)中,需要按顺序完成:插件声明、编译器选项、依赖声明、数据库版本。以下展示完整的配置示例。

2.2.1 添加 KSP/KAPT 插件

// 项目根 build.gradle.kts(或 settings.gradle.kts)
plugins {
    id("com.google.devtools.ksp") version "1.9.22-1.0.17" apply false
    // 如果使用 KAPT(不推荐新项目):
    // id("org.jetbrains.kotlin.kapt") version "1.9.22" apply false
}

// 模块 build.gradle.kts
plugins {
    id("com.android.application")
    id("org.jetbrains.kotlin.android")
    id("com.google.devtools.ksp") // 或 id("org.jetbrains.kotlin.kapt")
}

2.2.2 定义 room_version 变量

// 在模块 build.gradle.kts 的 android 块之前或项目级 gradle.properties
val room_version = "2.6.1"

2.2.3 添加 Room 依赖(使用 KSP)

dependencies {
    implementation("androidx.room:room-runtime:$room_version")
    ksp("androidx.room:room-compiler:$room_version")
    // 可选:Kotlin 协程扩展
    implementation("androidx.room:room-ktx:$room_version")
    // 可选:RxJava3 支持
    implementation("androidx.room:room-rxjava3:$room_version")
}

2.2.4 如果使用 KAPT,只需将 ksp 替换为 kapt:

dependencies {
    implementation("androidx.room:room-runtime:$room_version")
    kapt("androidx.room:room-compiler:$room_version")
}

2.3 编译器选项:schema 导出与日志

Room 编译器支持通过 arguments 块传递选项。常用配置:

  • room.schemaLocation:指定导出的数据库 Schema JSON 文件输出目录(用于版本迁移验证)。
  • room.incremental:开启增量编译。
  • room.expandProjection:展开投影查询的结果类型(避免运行时反射)。
android {
    // ...
    defaultConfig {
        // 可选:在编译时注入 schema 路径(用于数据库版本迁移测试)
    }
}

ksp {
    arg("room.schemaLocation", "$projectDir/schemas")
    arg("room.incremental", "true")
    arg("room.expandProjection", "true")
}

// 如果使用 KAPT,则放在 kapt 块中(语法略有不同):
// kapt {
//     arguments {
//         arg("room.schemaLocation", "$projectDir/schemas")
//     }
// }
生产环境注意:导出 Schema 文件会暴露表结构,建议在 release 构建中关闭。可通过条件判断实现:if (project.hasProperty("roomSchema")) {...}。

2.4 数据库版本声明

数据库版本通过 @Database 注解的 version 参数声明。但版本号的增量和迁移逻辑必须在编译前规划好。Gradle 中可以通过 buildConfigField 将版本暴露给 Java/Kotlin 代码。

// build.gradle.kts
android {
    defaultConfig {
        buildConfigField("int", "DATABASE_VERSION", "2")
    }
}

// 在 @Database 注解中使用:
// @Database(entities = [User::class], version = BuildConfig.DATABASE_VERSION)

版本控制的四个关键规则:

  1. 起始版本:第一个发布版本从 1 开始。
  2. 每次修改实体或 DAO 查询逻辑后:必须递增版本号。
  3. 版本号与迁移类名对应:推荐使用 Migration(1, 2) 形式,startVersion 和 endVersion 必须与声明一致。
  4. 破坏性迁移:如果无法提供 Migration 类,必须设置 fallbackToDestructiveMigration(),但会丢失已有数据。

2.5 整体 Gradle 配置流程(SVG 流程图)

Room 项目依赖配置流程 选择 KSP / KAPT 添加 KSP/KAPT 插件 定义 room_version 声明 Room 依赖 配置 compiler 选项 常见错误避坑 • ksp 与 kapt 不能同时启用 • room_version 必须一致 • schema 路径写成路径 • 版本号不递增导致迁移失败

2.6 调试与常见错误

错误现象根因解决方案
编译异常:cannot find symbol class _*ImplRoom 编译器未运行(插件或依赖缺失)检查是否添加了 ksp 或 kapt 依赖,并且插件 id 正确
运行崩溃:Migration didn't properly handle: users数据库版本与 Migration 对象不匹配,或迁移方法漏处理字段检查 @Database version 与 Migration startVersion/endVersion;导出 schema 对比差异
构建缓慢 / OOMKAPT 内存不足或未启用增量编译切到 KSP;设置 org.gradle.jvmargs=-Xmx2048m
Schema 导出目录报错路径使用了路径或不存在使用 $projectDir/schemas 相对路径,并确保目录存在
性能优化提示:使用 KSP 后,将 room.incremental 设为 true,并配合 Gradle 配置缓存。对于包含大量实体(超过 100 个)的项目,考虑拆分模块以降低单次编译负担。

2.7 部署时的版本管理

当应用发布到 Google Play 后,已有用户数据库版本是固定的。后续更新时,必须遵循:

  • 新版本号 > Play 商店已发布的最大版本号。
  • 必须提供从上一个版本到新版本的 Migration 对象,或者使用 fallbackToDestructiveMigration()(数据丢失)。
  • 建议在 build.gradle.kts 中定义常量,并与 @Database 注解保持同步。
// Migration 示例(自动版本声明)
@Database(entities = [User::class, Book::class], version = 3)
abstract class AppDatabase : RoomDatabase() {
    companion object {
        val MIGRATION_2_3 = object : Migration(2, 3) {
            override fun migrate(db: SupportSQLiteDatabase) {
                db.execSQL("ALTER TABLE users ADD COLUMN age INTEGER NOT NULL DEFAULT 0")
            }
        }
    }
}

总结:Gradle 配置是 Room 项目的基石,选择正确的处理机制、定义统一的版本变量、配置编译器选项和版本声明,能避免大部分编译和运行时问题。下一章节将进入实体(Entity)的设计。