Room 3.0 to aktualizacja do wersji głównej, która przenosi bibliotekę do Kotlin-first. Obsługuje Kotlin Multiplatform (KMP), wymaga Kotlin Symbol Processing (KSP) i wymusza używanie współprogramów w przypadku operacji asynchronicznych.
Aby zapobiec problemom ze zgodnością z istniejącymi aplikacjami Room 2.x i zależnościami przechodnimi, Room 3.0 znajduje się w nowym pakiecie: androidx.room3.
Z tego przewodnika dowiesz się, jak przeprowadzić migrację z implementacji Room 2.x do Room 3.0.
Najważniejsze zmiany w Room 3.0
Zanim rozpoczniesz migrację, zapoznaj się z najważniejszymi różnicami:
- Nowy pakiet i artefakty: wszystkie klasy znajdują się w
androidx.room3. Artefakty używają prefiksuroom3, np.androidx.room3:room3-runtime. - Tylko Kotlin i KSP: Room 3.0 nie obsługuje generowania kodu Java. Zamiast KAPT lub procesorów adnotacji Java używaj KSP. Room 3.0 nadal obsługuje źródła Java jako dane wejściowe.
- Współprogramy: funkcje DAO muszą być funkcjami
suspend, z wyjątkiem typów obserwowalnych.CoroutineContextzastępuje wykonawców. - Brak SupportSQLite:
SQLiteDriverinterfejsy API obsługują Room. Room usuwaSupportSQLiteDatabasez podstawowych interfejsów API. - Zmiany w interfejsie API: migracje i wywołania zwrotne bazy danych używają
SQLiteConnectionzamiastSupportSQLiteDatabase. - Konwertery typów reaktywnych: typy zwracane RxJava, LiveData, Guava i Paging
wymagają zarejestrowania
@DaoReturnTypeConverters.
Zalecamy przeprowadzenie migracji w 2 odrębnych etapach: najpierw przygotuj i zmodernizuj bazę kodu w Room 2.x, a następnie przejdź na Room 3.0.
Przygotowanie i modernizacja w Room 2.x
Przed migracją do Room 3.0 możesz wykonać większość prac związanych z modernizacją, aktualizując do bieżącej wersji Room 2.x, np. Room 2.8. Room 2.8 obsługuje Kotlin Multiplatform (KMP) i zawiera wiele interfejsów API sterowników, których używa Room 3.0.
Aktualizacja do Room 2.8 lub nowszej wersji
Zaktualizuj konfigurację kompilacji, aby używać bieżącej wersji 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" }
Migracja z KAPT do KSP
Room 3.0 nie obsługuje procesorów adnotacji Java ani KAPT. Musisz używać Kotlin Symbol Processing (KSP). Możesz dokonać tego przejścia, gdy nadal używasz Room 2.x.
W pliku
build.gradle.ktsmodułu zastosuj wtyczkę KSP:plugins { id("com.google.devtools.ksp") version "<ksp_version>" }Upewnij się, że wersja KSP jest zgodna z wersją Kotlin.
Zastąp
kaptlubannotationProcessorprzezkspw zależności od kompilatora Room:dependencies { implementation(libs.androidx.room.runtime) ksp(libs.androidx.room.compiler) }
Wprowadzenie współprogramów
Room 3.0 wymaga współprogramów do operacji asynchronicznych.
- Zaktualizuj DAO: wszystkie funkcje DAO muszą być funkcjami
suspend, chyba że zwracają obserwowalny typ reaktywny, np.Flowlub typy RxJava.
// 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>
}
- Jeśli skonfigurowano
RoomDatabaseza pomocą niestandardowegoExecutordo wykonywania operacji na bazie danych, przeprowadź migrację doCoroutineContextza pomocąsetQueryCoroutineContextw konstruktorze:
Room.databaseBuilder<AppDatabase>(context, "db")
.setQueryCoroutineContext(Dispatchers.IO)
.build()
Wprowadzenie interfejsów API sterowników i unikanie Support SQLite
Room 3.0 jest w pełni obsługiwany przez SQLiteDriver i nie obsługuje już SupportSQLiteDatabase w swoich podstawowych interfejsach API.
Jeśli nie wywołasz setDriver, aby ustawić SQLiteDriver w konstruktorze bazy danych, Room 2.8 będzie działać w trybie zgodności, w którym działają zarówno interfejsy API Support SQLite, jak i sterowników. Ten tryb zgodności umożliwia stopniowe przekształcanie bazy kodu przed włączeniem sterownika.
- Konwertowanie migracji: przeprowadź migrację podklas
MigrationiAutoMigrationSpec, aby używaćSQLiteConnectionzamiastSupportSQLiteDatabase.
// 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"
)
}
}
- Konwertowanie wywołań zwrotnych bazy danych: zaktualizuj
RoomDatabase.Callbackimplementacje, aby używać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) {
// ...
}
}
- Konwertowanie funkcji DAO
@RawQuery: w przypadku funkcji oznaczonych adnotacją@RawQueryużywajRoomRawQueryzamiastSupportSQLiteQuery:
// Before (SupportSQLiteQuery)
@Dao
interface UserDao {
@RawQuery
fun getUser(query: SupportSQLiteQuery): User
}
// After (RoomRawQuery)
@Dao
interface UserDao {
@RawQuery
suspend fun getUser(query: RoomRawQuery): User
}
Możesz utworzyć RoomRawQuery w czasie działania:
val query = RoomRawQuery(
sql = "SELECT * FROM User WHERE id = ?",
onBindStatement = { statement ->
statement.bindInt(1, userId)
}
)
- Konwertowanie interfejsów API transakcji: zastąp bloki tylko na Androidzie
withTransactionirunInTransactionprzezwithWriteTransactionlubwithReadTransaction:
// Before (withTransaction)
db.withTransaction {
// perform database operations
}
// After (withWriteTransaction - Room 2.8)
import androidx.room.withWriteTransaction
db.withWriteTransaction {
// perform database operations
}
Jeśli potrzebujesz bezpośredniego dostępu do połączenia transakcji na niskim poziomie, możesz też użyć useWriterConnection z immediateTransaction.
- Unikanie bezpośredniego użycia
SupportSQLiteDatabase: jeśli masz obszerny starszy kod, który nadal wymagaSupportSQLiteDatabasei nie możesz go jeszcze przenieć , użyj artefaktu zgodnościandroidx.room:room-sqlite-wrapper:
dependencies {
implementation("androidx.room:room-sqlite-wrapper:$roomVersion")
}
Następnie użyj getSupportWrapper, aby uzyskać SupportSQLiteDatabase z instancji bazy danych Room:
import androidx.room.support.getSupportWrapper
val legacyDb: SupportSQLiteDatabase = roomDatabase.getSupportWrapper()
- Ustawianie sterownika SQLite: po przeniesieniu wszystkich zastosowań interfejsu API Room do interfejsów API sterowników
skonfiguruj sterownik, np.
BundledSQLiteDriverlubAndroidSQLiteDriver, wywołującsetDriverw konstruktorzeRoomDatabase:
import androidx.sqlite.driver.bundled.BundledSQLiteDriver
val db = Room.databaseBuilder<AppDatabase>(context, "db")
.setDriver(BundledSQLiteDriver())
.build()
Wprowadzenie śledzenia unieważnień opartego na przepływie
Room 2.8 wprowadza interfejs API InvalidationTracker.createFlow. Użyj tego interfejsu API, aby przeprowadzić migrację ze starszych implementacji InvalidationTracker.Observer, gdy nadal używasz Room 2.x. Przygotowuje to bazę kodu do Room 3.0, która całkowicie usuwa 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()
}
Migracja do Room 3.0
Po zmodernizowaniu aplikacji w Room 2.x przejście na Room 3.0 wymaga zaktualizowania zależności, importów pakietów i wywołań zwrotnych bazy danych.
Aktualizowanie zależności i importów pakietów
- W konfiguracji kompilacji zastąp
androidx.roomzależności przezandroidx.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" }
- Zaktualizuj blok zależności:
dependencies {
implementation(libs.androidx.room3.runtime)
ksp(libs.androidx.room3.compiler)
}
- Zaktualizuj importy pakietów. Zastąp
import androidx.room.*przezimport androidx.room3.*.
Aktualizowanie interfejsów API konwertera typów
Room 3.0 zmienia nazwy interfejsów API konwertera typów, aby wyjaśnić ich użycie do konwertowania wartości kolumn i uniknąć pomyłek z konwerterami typów zwracanych przez DAO.
Zaktualizuj te adnotacje i funkcje w bazie kodu:
- Zmień nazwę
@TypeConverterna@ColumnTypeConverter. - Zmień nazwę
@TypeConvertersna@ColumnTypeConverters. - Zmień nazwę
@ProvidedTypeConverterna@ProvidedColumnTypeConverter. - Zmień nazwę
RoomDatabase.Builder.addTypeConverternaaddColumnTypeConverter.
Przykład:
// 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()
Aktualizowanie wywołań zwrotnych do funkcji zawieszania
W Room 3.0 wywołania zwrotne bazy danych i migracje używają SQLiteConnection i są funkcjami suspend.
- Zaktualizuj ręczne klasy
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"
)
}
}
- Zaktualizuj implementacje
RoomDatabase.Callback:
val callback = object : RoomDatabase.Callback() {
override suspend fun onCreate(connection: SQLiteConnection) {
// ...
}
}
Rejestrowanie konwerterów typów zwracanych przez DAO
W Room 3.0 reaktywne zwracane typy, takie jak RxJava, LiveData, Guava i Paging, wymagają zarejestrowania konwerterów zwracanych typów przez DAO za pomocą @DaoReturnTypeConverters.
import androidx.room3.paging.PagingSourceDaoReturnTypeConverter
@Dao
@DaoReturnTypeConverters(PagingSourceDaoReturnTypeConverter::class)
interface UserDao {
@Query("SELECT * FROM User")
fun getAllPaginated(): PagingSource<Int, User>
}
- Paging (
PagingSource): zarejestrujPagingSourceDaoReturnTypeConverterz artefaktuandroidx.room3:room3-paging. - RxJava (
Observable,Flowable,Single,Maybe,Completable): zarejestrujRxDaoReturnTypeConvertersz artefaktuandroidx.room3:room3-rxjava3. - Guava (
ListenableFuture): zarejestrujGuavaDaoReturnTypeConverterz artefaktuandroidx.room3:room3-guava. - LiveData (
LiveData): zarejestrujLiveDataDaoReturnTypeConverterz artefaktuandroidx.room3:room3-livedata.
Sprawdzanie usunięcia obserwatora InvalidationTracker
Room 3.0 całkowicie usuwa InvalidationTracker.Observer i powiązane metody rejestracji, takie jak addObserver i removeObserver.
Jeśli nie przeprowadzono jeszcze przejścia na przepływy współprogramów w
etapie 1, musisz przenieść wszystkie Observer zastosowania do
createFlow:
val userFlow = db.invalidationTracker.createFlow("User").map { _ ->
userDao.getAllUsers()
}