Room 3.0 to aktualizacja do wersji głównej, która sprawia, że biblioteka jest w pierwszej kolejności przeznaczona do używania z Kotlinem. 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 jako priorytet: 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 bieżącą wersję 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 korzystasz z 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 przypadku zależności 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
}
RoomRawQuery możesz utworzyć 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 dostępne tylko na Androidzie
withTransactionirunInTransactionprzezuseWriterConnectioniimmediateTransaction:
// Before (withTransaction)
db.withTransaction {
// perform database operations
}
// After (useWriterConnection - Room 2.8)
import androidx.room.useWriterConnection
import androidx.room.immediateTransaction
db.useWriterConnection { connection ->
connection.immediateTransaction {
// perform database operations
}
}
- 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ę z implementacji starszych wersji InvalidationTracker.Observer, gdy nadal korzystasz z 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 konwerterów typów
Room 3.0 zmienia nazwy interfejsów API konwerterów 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 zawieszających
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 w kroku 1 nie przeprowadzono jeszcze migracji do przepływów współprogramów, musisz przenieść wszystkie Observer zastosowania do
createFlow:
val userFlow = db.invalidationTracker.createFlow("User").map { _ ->
userDao.getAllUsers()
}