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.
Ключевые изменения в комнате 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заменяет исполнители. - Нет поддержки SQLite : API
SQLiteDriverподдерживают Room. Room удаляетSupportSQLiteDatabaseиз основных API. - Изменения в API : Для миграций и обратных вызовов к базе данных используется
SQLiteConnectionвместоSupportSQLiteDatabase. - Для конвертеров реактивных типов : возвращаемых типов RxJava, LiveData, Guava и Paging необходимо зарегистрировать аннотацию
@DaoReturnTypeConverters.
Мы рекомендуем миграцию в два отдельных этапа: сначала подготовка и модернизация вашей кодовой базы в Room 2.x, а затем переход на Room 3.0.
Подготовка и модернизация в комнате 2.x
Перед переходом на Room 3.0 большую часть работ по модернизации можно выполнить, обновив систему до текущей версии Room 2.x, например, Room 2.8. Room 2.8 поддерживает Kotlin Multiplatform (KMP) и включает в себя множество API драйверов, используемых в Room 3.0.
Обновите до версии Room 2.8 и выше.
Обновите конфигурацию сборки, чтобы использовать текущую версию 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наkspдля зависимости компилятора Room:dependencies { implementation(libs.androidx.room.runtime) ksp(libs.androidx.room.compiler) }
Внедрить сопрограммы
Для асинхронных операций в Room 3.0 требуются сопрограммы.
- Обновите ваши DAO: если они не возвращают наблюдаемый реактивный тип, например, типы
Flowили RxJava, все функции DAO должны быть функциямиsuspendfunctions).
// 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>
}
- Если вы настроили
RoomDatabaseс использованием пользовательскогоExecutorдля выполнения операций с базой данных, перейдите наCoroutineContext, используяsetQueryCoroutineContextв построителе:
Room.databaseBuilder<AppDatabase>(context, "db")
.setQueryCoroutineContext(Dispatchers.IO)
.build()
Используйте API драйверов и избегайте поддержки SQLite.
В Room 3.0 полностью используется SQLiteDriver , и в основных API больше не поддерживается SupportSQLiteDatabase .
Если вы не вызываете setDriver для установки SQLiteDriver в вашем построителе базы данных, Room 2.8 работает в режиме совместимости, в котором функционируют как API поддержки 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) {
// ...
}
}
- Преобразование функций DAO с аннотацией
@RawQuery: Для функций, аннотированных@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 транзакций : Замените блоки
withTransactionиrunInTransaction, используемые только в Android, на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 для получения объекта SupportSQLiteDatabase из вашего экземпляра базы данных Room:
import androidx.room.support.getSupportWrapper
val legacyDb: SupportSQLiteDatabase = roomDatabase.getSupportWrapper()
- Настройка драйвера SQLite : После переноса всех способов использования Room API на API драйверов, настройте драйвер, например
BundledSQLiteDriverилиAndroidSQLiteDriver, вызвавsetDriverв вашем конструктореRoomDatabase:
import androidx.sqlite.driver.bundled.BundledSQLiteDriver
val db = Room.databaseBuilder<AppDatabase>(context, "db")
.setDriver(BundledSQLiteDriver())
.build()
Внедрить отслеживание аннулирования на основе потока данных.
В Room 2.8 представлен API InvalidationTracker.createFlow . Используйте этот API для перехода от устаревших реализаций InvalidationTracker.Observer , оставаясь при этом в Room 2.x. Это подготовит ваш код к 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()
}
Переместиться в комнату 3.0
После модернизации вашего приложения на Room 2.x переход на Room 3.0 включает в себя обновление зависимостей, импорт пакетов и обратных вызовов базы данных.
Обновите зависимости и импорт пакетов.
- В конфигурации сборки замените зависимости
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()
Обновите функции обратного вызова, чтобы приостанавливать их работу.
В комнате 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, требуется регистрация преобразователей типов возвращаемых значений DAO с помощью @DaoReturnTypeConverters .
import androidx.room3.paging.PagingSourceDaoReturnTypeConverter
@Dao
@DaoReturnTypeConverters(PagingSourceDaoReturnTypeConverter::class)
interface UserDao {
@Query("SELECT * FROM User")
fun getAllPaginated(): PagingSource<Int, User>
}
- Paging (
PagingSource) : ЗарегистрируйтеPagingSourceDaoReturnTypeConverterиз артефактаandroidx.room3:room3-paging. - RxJava (
Observable,Flowable,Single,Maybe,Completable) : ЗарегистрируйтеRxDaoReturnTypeConvertersиз артефактаandroidx.room3:room3-rxjava3. - Guava (
ListenableFuture) : ЗарегистрируйтеGuavaDaoReturnTypeConverterиз артефактаandroidx.room3:room3-guava. - LiveData (
LiveData) : ЗарегистрируйтеLiveDataDaoReturnTypeConverterиз артефактаandroidx.room3:room3-livedata.
Проверка удаления наблюдателя InvalidationTracker
В Room 3.0 полностью удалены InvalidationTracker.Observer и связанные с ним методы регистрации, такие как addObserver и removeObserver .
Если вы еще не перешли на сопрограммы на первом этапе , вам необходимо перенести все случаи использования Observer в createFlow :
val userFlow = db.invalidationTracker.createFlow("User").map { _ ->
userDao.getAllUsers()
}