Room 3.0 是一项主要版本更新,可将库转换为 Kotlin 优先。它支持 Kotlin Multiplatform (KMP),需要 Kotlin Symbol Processing (KSP),并强制使用协程进行异步操作。
为避免与现有 Room 2.x 应用和传递性依赖项出现兼容性问题,Room 3.0 位于新软件包 androidx.room3 中。
本指南概述了将现有 Room 2.x 实现迁移到 Room 3.0 所需的步骤。
Room 3.0 中的主要变更
在开始迁移之前,请先熟悉以下主要区别:
- 新软件包和制品:所有类都位于
androidx.room3中。制品使用room3前缀,例如androidx.room3:room3-runtime。 - 仅限 Kotlin 和 KSP:Room 3.0 不支持 Java 代码生成。使用 KSP 而不是 KAPT 或 Java 注释处理器。Room 3.0 仍然支持将 Java 源作为输入。
- 协程优先:DAO 函数必须是
suspend函数,可观测类型除外。CoroutineContext取代了执行器。 - 无 SupportSQLite:
SQLiteDriverAPI 支持 Room。Room 从核心 API 中移除了SupportSQLiteDatabase。 - API 变更:迁移和数据库回调使用
SQLiteConnection而不是SupportSQLiteDatabase。 - 响应式类型的转换器:RxJava、LiveData、Guava 和 Paging 返回类型要求您注册
@DaoReturnTypeConverters。
我们建议分两个不同的阶段进行迁移:首先在 Room 2.x 中准备并更新您的代码库,然后切换到 Room 3.0。
在 Room 2.x 中准备和实现现代化
在迁移到 Room 3.0 之前,您可以通过更新到当前的 Room 2.x 版本(例如 Room 2.8)来完成大部分现代化改造工作。Room 2.8 支持 Kotlin Multiplatform (KMP),并包含 Room 3.0 使用的许多驱动程序 API。
更新到 Room 2.8 及更高版本
更新 build 配置以使用当前的 Room 2.x 版本:
[versions]
room2 = "2.8.4" # Use the current Room 2.8 version
[libraries]
androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "room2" }
androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "room2" }
从 KAPT 迁移到 KSP
Room 3.0 不支持 Java 注解处理器或 KAPT。您必须使用 Kotlin Symbol Processing (KSP)。您可以在仍使用 Room 2.x 的情况下进行此过渡。
在模块的
build.gradle.kts中,应用 KSP 插件:plugins { id("com.google.devtools.ksp") version "<ksp_version>" }确保 KSP 版本与您的 Kotlin 版本兼容。
将
kapt或annotationProcessor替换为 Room 编译器依赖项的ksp:dependencies { implementation(libs.androidx.room.runtime) ksp(libs.androidx.room.compiler) }
采用协程
Room 3.0 要求使用协程执行异步操作。
- 更新您的 DAO:除非它们返回可观测的响应式类型(例如
Flow或 RxJava 类型),否则所有 DAO 函数都必须是suspend函数。
// Before (Blocking)
@Dao
interface UserDao {
@Query("SELECT * FROM User")
fun getAll(): List<User>
}
// After (Suspend)
@Dao
interface UserDao {
@Query("SELECT * FROM User")
suspend fun getAll(): List<User>
}
- 如果您使用自定义
Executor配置了RoomDatabase以执行数据库操作,请使用构建器上的setQueryCoroutineContext迁移到CoroutineContext:
Room.databaseBuilder<AppDatabase>(context, "db")
.setQueryCoroutineContext(Dispatchers.IO)
.build()
采用驱动程序 API 并避免支持 SQLite
Room 3.0 完全由 SQLiteDriver 提供支持,不再在其核心 API 中支持 SupportSQLiteDatabase。
如果您未调用 setDriver 在数据库构建器上设置 SQLiteDriver,Room 2.8 将在兼容模式下运行,其中 Support SQLite 和驱动程序 API 均可正常运行。借助此兼容性模式,您可以在启用驱动程序之前逐步转换代码库。
- 转换迁移:迁移
Migration和AutoMigrationSpec子类以使用SQLiteConnection而不是SupportSQLiteDatabase。
// Before (SupportSQLiteDatabase)
val MIGRATION_1_2 = object : Migration(1, 2) {
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL("ALTER TABLE User ADD COLUMN age INTEGER DEFAULT 0 NOT NULL")
}
}
// After (SQLiteConnection - Room 2.8)
import androidx.sqlite.SQLiteConnection
import androidx.sqlite.execSQL
val MIGRATION_1_2 = object : Migration(1, 2) {
override fun migrate(connection: SQLiteConnection) {
connection.execSQL(
"ALTER TABLE User ADD COLUMN age INTEGER DEFAULT 0 NOT NULL"
)
}
}
- 转换数据库回调:更新
RoomDatabase.Callback实现以使用SQLiteConnection:
// Before (SupportSQLiteDatabase)
val callback = object : RoomDatabase.Callback() {
override fun onCreate(db: SupportSQLiteDatabase) {
// ...
}
}
// After (SQLiteConnection - Room 2.8)
val callback = object : RoomDatabase.Callback() {
override fun onCreate(connection: SQLiteConnection) {
// ...
}
}
- 转换
@RawQueryDAO 函数:对于带有@RawQuery注解的函数,请使用RoomRawQuery而不是SupportSQLiteQuery:
// Before (SupportSQLiteQuery)
@Dao
interface UserDao {
@RawQuery
fun getUser(query: SupportSQLiteQuery): User
}
// After (RoomRawQuery)
@Dao
interface UserDao {
@RawQuery
suspend fun getUser(query: RoomRawQuery): User
}
您可以在运行时构建 RoomRawQuery:
val query = RoomRawQuery(
sql = "SELECT * FROM User WHERE id = ?",
onBindStatement = { statement ->
statement.bindInt(1, userId)
}
)
- 转换交易 API:将仅限 Android 的
withTransaction和runInTransaction块替换为withWriteTransaction或withReadTransaction:
// Before (withTransaction)
db.withTransaction {
// perform database operations
}
// After (withWriteTransaction - Room 2.8)
import androidx.room.withWriteTransaction
db.withWriteTransaction {
// perform database operations
}
如果您需要直接对事务连接进行低级访问,也可以将 useWriterConnection 与 immediateTransaction 搭配使用。
- 避免直接使用
SupportSQLiteDatabase:如果您有大量仍需要SupportSQLiteDatabase的旧版代码,但暂时无法迁移,请使用androidx.room:room-sqlite-wrapper兼容性制品:
dependencies {
implementation("androidx.room:room-sqlite-wrapper:$roomVersion")
}
然后,使用 getSupportWrapper 从 Room 数据库实例中获取 SupportSQLiteDatabase:
import androidx.room.support.getSupportWrapper
val legacyDb: SupportSQLiteDatabase = roomDatabase.getSupportWrapper()
- 设置 SQLite 驱动程序:将所有 Room API 用法迁移到驱动程序 API 后,通过在
RoomDatabase构建器中调用setDriver来配置驱动程序,例如BundledSQLiteDriver或AndroidSQLiteDriver:
import androidx.sqlite.driver.bundled.BundledSQLiteDriver
val db = Room.databaseBuilder<AppDatabase>(context, "db")
.setDriver(BundledSQLiteDriver())
.build()
采用基于流程的失效追踪
Room 2.8 引入了 InvalidationTracker.createFlow API。使用此 API 可在仍使用 Room 2.x 的同时,从旧版 InvalidationTracker.Observer 实现方案迁移。这会为 Room 3.0 做好代码库准备,该版本会完全移除 Observer。
// Before (InvalidationTracker.Observer)
val observer = object : InvalidationTracker.Observer("User") {
override fun onInvalidated(tables: Set<String>) {
// reload user data
}
}
db.invalidationTracker.addObserver(observer)
// After (createFlow - Room 2.8)
val userFlow = db.invalidationTracker.createFlow("User").map { _ ->
userDao.getAllUsers()
}
迁移到 Room 3.0
在 Room 2.x 上实现应用现代化后,迁移到 Room 3.0 需要更新依赖项、软件包导入和数据库回调。
更新依赖项和软件包导入
- 在 build 配置中,将
androidx.room依赖项替换为androidx.room3:
[versions]
room3 = "3.0.0" # Use the current Room 3.0 version
[libraries]
androidx-room3-runtime = { module = "androidx.room3:room3-runtime", version.ref = "room3" }
androidx-room3-compiler = { module = "androidx.room3:room3-compiler", version.ref = "room3" }
- 更新您的依赖项代码块:
dependencies {
implementation(libs.androidx.room3.runtime)
ksp(libs.androidx.room3.compiler)
}
- 更新软件包导入。将
import androidx.room.*替换为import androidx.room3.*。
更新了类型转换器 API
Room 3.0 重命名了类型转换器 API,以明确其用于转换列值的用途,并避免与 DAO 返回类型转换器混淆。
更新代码库中的以下注释和函数:
- 已将
@TypeConverter重命名为@ColumnTypeConverter。 - 已将
@TypeConverters重命名为@ColumnTypeConverters。 - 已将
@ProvidedTypeConverter重命名为@ProvidedColumnTypeConverter。 - 已将
RoomDatabase.Builder.addTypeConverter重命名为addColumnTypeConverter。
示例:
// Before
@ProvidedTypeConverter
class Converters {
@TypeConverter
fun fromTimestamp(value: Long?): Date? = ...
}
@Database(entities = [User::class], version = 1)
@TypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase()
val db = Room.databaseBuilder<AppDatabase>(...)
.addTypeConverter(convertersInstance)
.build()
// After
import androidx.room3.ColumnTypeConverter
import androidx.room3.ColumnTypeConverters
import androidx.room3.ProvidedColumnTypeConverter
@ProvidedColumnTypeConverter
class Converters {
@ColumnTypeConverter
fun fromTimestamp(value: Long?): Date? = ...
}
@Database(entities = [User::class], version = 1)
@ColumnTypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase()
val db = Room.databaseBuilder<AppDatabase>(...)
.addColumnTypeConverter(convertersInstance)
.build()
将回调更新为挂起函数
在 Room 3.0 中,数据库回调和迁移使用 SQLiteConnection,并且是 suspend 函数。
- 更新手动
Migration类:
import androidx.sqlite.SQLiteConnection
import androidx.sqlite.async.executeSQL
val MIGRATION_1_2 = object : Migration(1, 2) {
override suspend fun migrate(connection: SQLiteConnection) {
connection.executeSQL(
"ALTER TABLE User ADD COLUMN age INTEGER DEFAULT 0 NOT NULL"
)
}
}
- 更新
RoomDatabase.Callback实现:
val callback = object : RoomDatabase.Callback() {
override suspend fun onCreate(connection: SQLiteConnection) {
// ...
}
}
注册 DAO 返回值类型转换器
在 Room 3.0 中,响应式返回值类型(例如 RxJava、LiveData、Guava 和 Paging)要求您使用 @DaoReturnTypeConverters 注册 DAO 返回值类型转换器。
import androidx.room3.paging.PagingSourceDaoReturnTypeConverter
@Dao
@DaoReturnTypeConverters(PagingSourceDaoReturnTypeConverter::class)
interface UserDao {
@Query("SELECT * FROM User")
fun getAllPaginated(): PagingSource<Int, User>
}
- 分页 (
PagingSource):从androidx.room3:room3-paging制品注册PagingSourceDaoReturnTypeConverter。 - RxJava (
Observable、Flowable、Single、Maybe、Completable):注册来自androidx.room3:room3-rxjava3制品的RxDaoReturnTypeConverters。 - Guava (
ListenableFuture):从androidx.room3:room3-guava制品注册GuavaDaoReturnTypeConverter。 - LiveData (
LiveData):从androidx.room3:room3-livedata制品注册LiveDataDaoReturnTypeConverter。
验证 InvalidationTracker 观察者移除
Room 3.0 完全移除了 InvalidationTracker.Observer 和相关的注册方法,例如 addObserver 和 removeObserver。
如果您尚未在第 1 阶段中过渡到协程流,则必须将所有 Observer 用法迁移到 createFlow:
val userFlow = db.invalidationTracker.createFlow("User").map { _ ->
userDao.getAllUsers()
}