首页 > 数据库 >Room 3.0包名重构与KMP迁移:项目升级踩坑经验

Room 3.0包名重构与KMP迁移:项目升级踩坑经验

来源:互联网 2026-07-08 08:38:17

Room3.0底层重构,包名改为androidx.room3,支持KMP跨平台等多种功能。此次升级需要处理支持SQLite库移除、迁移签名变更、注解处理器KAPT切换为KSP、以及显式设置SQLite驱动等关键变化。开发者需特别留意,建议先全量编译再渐进迁移以降低风险。

先说结论:Room 3.0 不是简单换个版本号,这是一次伤筋动骨的底层重构。7月1日 androidx.room3:room3-*:3.0.0 正式发布,包名、Maven 坐标、核心 API 全部更换,支持 KMP 后你的数据库代码能跑在 iOS 和 JVM 桌面上。但升级过程远比想象中折腾。

Room 3.0包名重构与KMP迁移:项目升级踩坑经验

为什么这次升级非同一般

这些年经手的库版本升级不算少——从 Room 2.5 到 2.6、Lifecycle 升级、Compose 版本对齐——基本都是改个版本号,修几个 deprecated 调用就完事了。但 Room 3.0 完全不一样。Google 直接把整个库搬到了新的 namespace:androidx.room 变成了 androidx.room3,Maven Group ID 从 androidx.room:room-runtime 变成了 androidx.room3:room3-runtime。 这不是随便改着玩的。Google 的解释是:很多现有库(比如 WorkManager)会传递依赖 Room 2.x,如果 3.0 还在原包名上升版本,会立刻引发二进制冲突。新包名相当于创建了一个“平行世界”,允许新旧版本在同一个项目里共存——理论上。实际操作中,这个“共存”本身就是个坑,后面细说。 真正让人决定升级的动力是 KMP。项目里数据库层的代码量不小,如果 Entity 和 DAO 能直接在 iOS 端复用,客户端团队至少能砍掉 30% 的重复代码。Room 3.0 底层完全脱离了 Android 的 SupportSQLite API,改用新的 androidx.sqlite 驱动接口,这才是 KMP 能跑起来的关键。

依赖声明改完,满屏红色报错

改完版本号,第一步当然是改依赖声明。把 build.gradle.kts 里的 Room 相关依赖全换了:
// 之前
implementation("androidx.room:room-runtime:2.6.1")
implementation("androidx.room:room-ktx:2.6.1")
ksp("androidx.room:room-compiler:2.6.1")
// 改后
implementation("androidx.room3:room3-runtime:3.0.0")
implementation("androidx.room3:room3-ktx:3.0.0")
ksp("androidx.room3:room3-compiler:3.0.0")
改完一 sync,满屏红色报错。所有 import 语句都是旧的:import androidx.room.Entityimport androidx.room.Daoimport androidx.room.Query……你项目里用了多少 Room 注解,就有多少 import 要改。 通过 IDE 的全局替换能搞定大部分——把 androidx.room. 替换成 androidx.room3.。但这里有个容易漏的点:字符串里的包名引用。比如在 TypeConverter 里用 Gson 反序列化,有个地方写了硬编码的类名引用,全局替换不会扫到字符串。跑单测的时候才发现 NPE,排查半天才定位到。 还有个更隐蔽的问题:项目里有个自定义的 Migration,里面用到了 SupportSQLiteDatabase。这个类整个被干掉了,Room 3.0 换成了 SQLiteConnection。Migration 代码直接编译不过。

最痛的坑:SupportSQLite 全家被移除

这可能是 Room 3.0 里迁移成本最高的一个变化。 Room 2.x 的底层完全绑定在 Android 的 SupportSQLite API 上——SupportSQLiteDatabaseSupportSQLiteOpenHelperSupportSQLiteQuery,这些是 Room 和 Android SQLite 之间的桥梁。Room 3.0 为了 KMP,把这些全部替换成了新的 androidx.sqlite 驱动 API。 影响面很大。至少碰到了这几个地方: 数据库构建器必须设置 Driver
// Room 2.x
val db = Room.databaseBuilder(context, AppDatabase::class.ja va, "app.db")
    .addMigrations(MIGRATION_1_2)
    .build()
// Room 3.0 —— 必须显式提供 SQLiteDriver
val db = Room.databaseBuilder(context, "app.db")
    .setDriver(BundledSQLiteDriver())  // Android 上用这个
    .addMigrations(MIGRATION_1_2)
    .build()
不加 setDriver() 直接运行时崩溃。报错信息倒是挺明确:No SQLiteDriver provided。但问题是文档里这个变更写得很隐蔽,翻了好几页 release notes 才找到。 Migration 签名变了
// Room 2.x
val MIGRATION_1_2 = object : Migration(1, 2) {
    override fun migrate(db: SupportSQLiteDatabase) {
        db.execSQL("ALTER TABLE user ADD COLUMN age INTEGER")
    }
}
// Room 3.0
val MIGRATION_1_2 = object : Migration(1, 2) {
    override fun migrate(connection: SQLiteConnection) {
        connection.execSQL("ALTER TABLE user ADD COLUMN age INTEGER")
    }
}
migrate() 的参数从 SupportSQLiteDatabase 变成了 SQLiteConnection。如果 Migration 里只用了 execSQL(),改起来很简单。但项目里有个复杂的 Migration,用到了 db.beginTransaction() / db.setTransactionSuccessful() / db.endTransaction() 来手动控制事务。SQLiteConnection 上没有这套 API 了。 查了一下,Room 3.0 把事务控制也改了。RoomDatabase.runInTransaction() 变成了 withWriteTransaction()(suspend 函数),Migration 里的事务则由 Room 自动管理——你不应该再手动控制。把那几行事务代码删掉,让 Room 自己管,反而是正确的做法。但这个行为变化在迁移指南里没提,是对比了 Room 源码才确认的。 RoomDatabase.Callback 也变了 onCreate()onOpen() 的参数也从 SupportSQLiteDatabase 变成了 SQLiteConnection。在 onOpen() 里做了数据库初始化的预填充逻辑,改完参数类型还要检查 SQLiteConnection 上有没有之前用的方法。大部分都有对应,但 compileStatement() 的返回类型变了,链式调用那里也需要调整。 改到这里的时候,已经能明显感觉到这次升级的重心。SupportSQLite 的移除不是一个点,它是一整张网的替换,每个节点都可能踩到。而且编译器报错只会告诉你当前这行有问题,不会告诉你还有多少行等着你。

一个没想到的坑:KAPT 被彻底干掉

Room 3.0 只支持 KSP,不再支持 Java 注解处理器(APT)和 KAPT。 项目里其实已经在用 KSP 了,所以本以为这个变化跟自己没关系。结果还是踩到了——有个老模块还在用 kapt("androidx.room:room-compiler:2.6.1"),新 Room 3.0 的 compiler 根本不注册 KAPT 处理器。sync 没问题,编译时 KAPT 找不到处理器直接报错。 更坑的是,这个老模块平时很少动,第一次编译没跑到那个模块就没发现。后来跑全量构建才爆出来。教训:迁移的时候一定要跑全量构建,不要只编译你改过的模块。 把 kapt 换成 ksp 就行了:
// 之前
kapt("androidx.room3:room3-compiler:3.0.0")
// 改后
ksp("androidx.room3:room3-compiler:3.0.0")
简单是简单,但如果你项目里有其他库也依赖 KAPT(比如 Dagger/Hilt 的老版本),KSP 和 KAPT 混用本身也可能出问题。好在 Hilt 早就支持 KSP 了,顺便一起迁了。

KMP 跨平台:能跑,但有条件

把 Android 端的 Room 迁移搞定后,试了一下 KMP 共享模块的配置。这是 Room 3.0 的核心卖点——同一个 Entity 和 DAO 在 Android、iOS、JVM 桌面、Web 上都能用。 共享模块的 build.gradle.kts 大概长这样:
kotlin {
    androidTarget()
    iosX64()
    iosArm64()
    iosSimulatorArm64()
    sourceSets {
        commonMain.dependencies {
            implementation("androidx.room3:room3-runtime:3.0.0")
        }
    }
}
但这里有个前提条件:你需要为每个平台提供对应的 SQLiteDriver。Android 用 BundledSQLiteDriver,iOS 需要用 NativeSQLiteDriver(来自 androidx.sqlite:sqlite-driver-native),JVM 桌面用 JDBC 驱动的那个。 这意味着你的 common 代码里不能直接 Room.databaseBuilder,因为 Driver 是平台相关的。常见做法是在 expect/actual 里提供数据库实例的创建逻辑。 项目目前只在 Android 和 JVM 桌面端跑,iOS 端还没接入。Android 端一切正常,JVM 桌面端配了 JDBC Driver 后也能跑。但注意 JDBC Driver 需要你手动引入 SQLite JDBC 依赖,Room 不会自动帮你拉。 另外一个容易忽略的点:Room 3.0 的 KMP 支持要求你的 Entity 和 DAO 写在 commonMain 里,不能写在 androidMain 里。如果你的项目之前把 Entity 放在 Android 模块里,迁移到 KMP 共享模块需要把文件移过去,同时把 Android 特有的类型引用(比如 Context)去掉。这个工作量取决于你之前代码组织得怎么样——Entity 比较干净,只需要改 import,但如果 Entity 里有 Android 类型的字段,就得考虑怎么抽象了。

官方给的缓冲方案

Google 也知道这次迁移太猛了,所以提供了一个兼容库 androidx.room3:room3-sqlite-wrapper。通过它可以临时获取 SupportSQLiteDatabase 的包装实例,用来兼容那些暂时没法改的旧代码。
// 引入 wrapper 依赖
implementation("androidx.room3:room3-sqlite-wrapper:3.0.0")
// 使用
val supportDb = roomDatabase.getSupportWrapper()
supportDb.execSQL("...")
用了两天,然后把它删了。原因很简单:这个 wrapper 只是一个过渡方案,未来会被废弃。而且它的行为和真正的 SupportSQLiteDatabase 有微妙差异——比如事务的行为不完全一致,Migration 里用 wrapper 操作数据,偶现死锁。可能用法有问题,但既然最终都要迁到新 API,不如一步到位。

迁移顺序建议

回过头看,如果重新来一遍,会按这个顺序操作:先把所有 import 和依赖声明从 androidx.room 换成 androidx.room3,确保编译通过。然后处理 Migration——把 SupportSQLiteDatabase 换成 SQLiteConnection,删掉手动事务控制。接着改 Room.databaseBuilder,加上 setDriver(BundledSQLiteDriver())。再改 RoomDatabase.Callback。最后处理 KAPT 到 KSP 的切换。 每改一步就跑一次全量编译,别攒着一起改。SupportSQLite 移除影响的地方太散了,不改一个确认一个,后面排查成本会指数级增长。

值不值得升级

如果你的项目只跑 Android,而且 Room 2.x 用得好好的——不急。Room 2.x 还会持续维护,3.0 的 KMP 能力对你没有直接收益。强行升级只会增加工作量。 但如果你有跨平台的需求,或者项目正在规划 KMP 架构,Room 3.0 是必须要过的坎。早升级,代码量小的时候改起来更轻松。等到 Entity 和 DAO 堆积到上百个的时候再迁,那个痛苦指数会完全不一样。 还有一点:Room 3.0 的新包名设计意味着你可以渐进式迁移。项目里可以同时存在 Room 2.x 和 3.0 的依赖,老的模块继续用 2.x,新的模块用 3.0。不用一口气全改,先从独立模块开始试水是更稳的策略。 就写到这吧。Room 3.0 的 KMP 故事才刚开始,后面有新的踩坑经验再聊。

侠游戏发布此文仅为了传递信息,不代表侠游戏网站认同其观点或证实其描述

热游推荐

更多
湘ICP备2026025700号-3 湘公网安备 43070302000280号
All Rights Reserved
本站为非盈利网站,不接受任何广告。本站所有软件,都由网友
上传,如有侵犯你的版权,请发邮件给xiayx666@163.com
抵制不良色情、反动、暴力游戏。注意自我保护,谨防受骗上当。
适度游戏益脑,沉迷游戏伤身。合理安排时间,享受健康生活。