Room3.0底层重构,包名改为androidx.room3,支持KMP跨平台等多种功能。此次升级需要处理支持SQLite库移除、迁移签名变更、注解处理器KAPT切换为KSP、以及显式设置SQLite驱动等关键变化。开发者需特别留意,建议先全量编译再渐进迁移以降低风险。
androidx.room3:room3-*:3.0.0 正式发布,包名、Maven 坐标、核心 API 全部更换,支持 KMP 后你的数据库代码能跑在 iOS 和 JVM 桌面上。但升级过程远比想象中折腾。

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.Entity、import androidx.room.Dao、import androidx.room.Query……你项目里用了多少 Room 注解,就有多少 import 要改。
通过 IDE 的全局替换能搞定大部分——把 androidx.room. 替换成 androidx.room3.。但这里有个容易漏的点:字符串里的包名引用。比如在 TypeConverter 里用 Gson 反序列化,有个地方写了硬编码的类名引用,全局替换不会扫到字符串。跑单测的时候才发现 NPE,排查半天才定位到。
还有个更隐蔽的问题:项目里有个自定义的 Migration,里面用到了 SupportSQLiteDatabase。这个类整个被干掉了,Room 3.0 换成了 SQLiteConnection。Migration 代码直接编译不过。
SupportSQLite API 上——SupportSQLiteDatabase、SupportSQLiteOpenHelper、SupportSQLiteQuery,这些是 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("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 了,顺便一起迁了。
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 类型的字段,就得考虑怎么抽象了。
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,不如一步到位。
androidx.room 换成 androidx.room3,确保编译通过。然后处理 Migration——把 SupportSQLiteDatabase 换成 SQLiteConnection,删掉手动事务控制。接着改 Room.databaseBuilder,加上 setDriver(BundledSQLiteDriver())。再改 RoomDatabase.Callback。最后处理 KAPT 到 KSP 的切换。
每改一步就跑一次全量编译,别攒着一起改。SupportSQLite 移除影响的地方太散了,不改一个确认一个,后面排查成本会指数级增长。
侠游戏发布此文仅为了传递信息,不代表侠游戏网站认同其观点或证实其描述