Room 3.0 adalah update versi utama yang mengalihkan library agar mengutamakan Kotlin. Library ini mendukung Multiplatform Kotlin (KMP), memerlukan Pemrosesan Simbol Kotlin (KSP), dan menerapkan coroutine untuk operasi asinkron.
Untuk mencegah masalah kompatibilitas dengan aplikasi Room 2.x yang ada dan dependensi transitif, Room 3.0 berada dalam paket baru: androidx.room3.
Panduan ini menguraikan langkah-langkah yang diperlukan untuk memigrasikan implementasi Room 2.x yang ada ke Room 3.0.
Perubahan utama di Room 3.0
Sebelum memulai migrasi, pahami perbedaan utamanya:
- Paket dan artefak baru: Semua class berada di
androidx.room3. Artefak menggunakan awalanroom3, sepertiandroidx.room3:room3-runtime. - Hanya Kotlin dan KSP: Room 3.0 tidak mendukung pembuatan kode Java. Gunakan KSP, bukan KAPT atau pemroses anotasi Java. Room 3.0 masih mendukung sumber Java sebagai input.
- Coroutine terlebih dahulu: Fungsi DAO harus berupa fungsi
suspendkecuali untuk jenis yang dapat diamati.CoroutineContextmenggantikan eksekutor. - Tidak ada SupportSQLite:
SQLiteDriverAPI mendukung Room. Room menghapusSupportSQLiteDatabasedari API inti. - Perubahan API: Migrasi dan callback database menggunakan
SQLiteConnectionbukanSupportSQLiteDatabase. - Konverter untuk jenis reaktif: Jenis nilai yang ditampilkan RxJava, LiveData, Guava, dan Paging
mengharuskan Anda mendaftarkan
@DaoReturnTypeConverters.
Sebaiknya lakukan migrasi dalam dua fase yang berbeda: pertama, siapkan dan modernkan codebase Anda di Room 2.x, lalu beralih ke Room 3.0.
Menyiapkan dan memodernkan di Room 2.x
Sebelum bermigrasi ke Room 3.0, Anda dapat melakukan sebagian besar pekerjaan modernisasi dengan mengupdate ke rilis Room 2.x saat ini, seperti Room 2.8. Room 2.8 mendukung Multiplatform Kotlin, atau KMP, dan menyertakan banyak driver API yang digunakan Room 3.0.
Mengupdate ke Room 2.8 dan yang lebih tinggi
Update konfigurasi build Anda untuk menggunakan rilis Room 2.x saat ini:
[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" }
Bermigrasi dari KAPT ke KSP
Room 3.0 tidak mendukung pemroses anotasi Java atau KAPT. Anda harus menggunakan Pemrosesan Simbol Kotlin (KSP). Anda dapat melakukan transisi ini saat masih menggunakan Room 2.x.
Di
build.gradle.ktsmodul Anda, terapkan plugin KSP:plugins { id("com.google.devtools.ksp") version "<ksp_version>" }Pastikan versi KSP kompatibel dengan versi Kotlin Anda.
Ganti
kaptatauannotationProcessordengankspuntuk dependensi pengompilasi Room:dependencies { implementation(libs.androidx.room.runtime) ksp(libs.androidx.room.compiler) }
Mengadopsi coroutine
Room 3.0 memerlukan coroutine untuk operasi asinkron.
- Update DAO Anda: Kecuali jika menampilkan jenis reaktif yang dapat diamati, seperti jenis
Flowatau RxJava, semua fungsi DAO harus berupa fungsisuspend.
// 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>
}
- Jika Anda mengonfigurasi
RoomDatabasedenganExecutorkustom untuk melakukan operasi database, migrasikan keCoroutineContextmenggunakansetQueryCoroutineContextpada builder:
Room.databaseBuilder<AppDatabase>(context, "db")
.setQueryCoroutineContext(Dispatchers.IO)
.build()
Mengadopsi driver API dan menghindari Support SQLite
Room 3.0 sepenuhnya didukung oleh SQLiteDriver dan tidak lagi mendukung SupportSQLiteDatabase di API intinya.
Jika Anda tidak memanggil setDriver untuk menetapkan SQLiteDriver pada builder database, Room 2.8 akan beroperasi dalam mode kompatibilitas tempat Support SQLite dan Driver API berfungsi. Mode kompatibilitas ini memungkinkan Anda mengonversi codebase secara bertahap sebelum mengaktifkan driver.
- Mengonversi migrasi: Migrasikan
MigrationdanAutoMigrationSpecsubclass untuk menggunakanSQLiteConnection, bukanSupportSQLiteDatabase.
// 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"
)
}
}
- Mengonversi callback database: Update
RoomDatabase.Callbackimplementasi untuk menggunakanSQLiteConnection:
// 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) {
// ...
}
}
- Mengonversi fungsi DAO
@RawQuery: Untuk fungsi yang dianotasi dengan@RawQuery, gunakanRoomRawQuery, bukanSupportSQLiteQuery:
// Before (SupportSQLiteQuery)
@Dao
interface UserDao {
@RawQuery
fun getUser(query: SupportSQLiteQuery): User
}
// After (RoomRawQuery)
@Dao
interface UserDao {
@RawQuery
suspend fun getUser(query: RoomRawQuery): User
}
Anda dapat membuat RoomRawQuery saat runtime:
val query = RoomRawQuery(
sql = "SELECT * FROM User WHERE id = ?",
onBindStatement = { statement ->
statement.bindInt(1, userId)
}
)
- Mengonversi API transaksi: Ganti blok
withTransactiondanrunInTransactionkhusus Android denganwithWriteTransactionatauwithReadTransaction:
// Before (withTransaction)
db.withTransaction {
// perform database operations
}
// After (withWriteTransaction - Room 2.8)
import androidx.room.withWriteTransaction
db.withWriteTransaction {
// perform database operations
}
Jika memerlukan akses tingkat rendah langsung ke koneksi transaksi, Anda juga dapat menggunakan useWriterConnection dengan immediateTransaction.
- Menghindari penggunaan langsung
SupportSQLiteDatabase: Jika Anda memiliki kode lama yang luas yang masih memerlukanSupportSQLiteDatabasedan Anda belum dapat memigrasikannya, gunakan artefak kompatibilitasandroidx.room:room-sqlite-wrapper:
dependencies {
implementation("androidx.room:room-sqlite-wrapper:$roomVersion")
}
Kemudian, gunakan getSupportWrapper untuk mendapatkan SupportSQLiteDatabase dari instance database Room Anda:
import androidx.room.support.getSupportWrapper
val legacyDb: SupportSQLiteDatabase = roomDatabase.getSupportWrapper()
- Menetapkan driver SQLite: Setelah memigrasikan semua penggunaan Room API ke driver
API, konfigurasikan driver, seperti
BundledSQLiteDriveratauAndroidSQLiteDriver, dengan memanggilsetDriverdi builderRoomDatabaseAnda:
import androidx.sqlite.driver.bundled.BundledSQLiteDriver
val db = Room.databaseBuilder<AppDatabase>(context, "db")
.setDriver(BundledSQLiteDriver())
.build()
Mengadopsi pelacakan pembatalan berbasis aliran
Room 2.8 memperkenalkan InvalidationTracker.createFlow API. Gunakan API ini untuk bermigrasi dari implementasi InvalidationTracker.Observer lama saat masih menggunakan Room 2.x. Hal ini akan menyiapkan codebase Anda untuk Room 3.0, yang sepenuhnya menghapus 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()
}
Bermigrasi ke Room 3.0
Setelah memodernkan aplikasi Anda di Room 2.x, transisi ke Room 3.0 melibatkan pembaruan dependensi, impor paket, dan callback database.
Memperbarui dependensi dan impor paket
- Dalam konfigurasi build Anda, ganti
androidx.roomdependensi denganandroidx.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" }
- Perbarui blok dependensi Anda:
dependencies {
implementation(libs.androidx.room3.runtime)
ksp(libs.androidx.room3.compiler)
}
- Perbarui impor paket Anda. Ganti
import androidx.room.*denganimport androidx.room3.*.
Memperbarui API konverter jenis
Room 3.0 mengganti nama API konverter jenis untuk mengklarifikasi penggunaannya dalam mengonversi nilai kolom dan untuk menghindari kebingungan dengan konverter jenis nilai yang ditampilkan DAO.
Perbarui anotasi dan fungsi berikut dalam codebase Anda:
- Ganti nama
@TypeConvertermenjadi@ColumnTypeConverter. - Ganti nama
@TypeConvertersmenjadi@ColumnTypeConverters. - Ganti nama
@ProvidedTypeConvertermenjadi@ProvidedColumnTypeConverter. - Ganti nama
RoomDatabase.Builder.addTypeConvertermenjadiaddColumnTypeConverter.
Contoh:
// 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()
Mengupdate callback ke fungsi penangguhan
Di Room 3.0, callback dan migrasi database menggunakan SQLiteConnection dan merupakan fungsi suspend.
- Perbarui class
Migrationmanual Anda:
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"
)
}
}
- Perbarui implementasi
RoomDatabase.CallbackAnda:
val callback = object : RoomDatabase.Callback() {
override suspend fun onCreate(connection: SQLiteConnection) {
// ...
}
}
Mendaftarkan konverter jenis nilai yang ditampilkan DAO
Di Room 3.0, jenis nilai yang ditampilkan reaktif, seperti RxJava, LiveData, Guava, dan Paging, mengharuskan Anda mendaftarkan konverter jenis nilai yang ditampilkan DAO menggunakan @DaoReturnTypeConverters.
import androidx.room3.paging.PagingSourceDaoReturnTypeConverter
@Dao
@DaoReturnTypeConverters(PagingSourceDaoReturnTypeConverter::class)
interface UserDao {
@Query("SELECT * FROM User")
fun getAllPaginated(): PagingSource<Int, User>
}
- Paging (
PagingSource): DaftarkanPagingSourceDaoReturnTypeConverterdari artefakandroidx.room3:room3-paging. - RxJava (
Observable,Flowable,Single,Maybe,Completable): DaftarkanRxDaoReturnTypeConvertersdari artefakandroidx.room3:room3-rxjava3. - Guava (
ListenableFuture): DaftarkanGuavaDaoReturnTypeConverterdari artefakandroidx.room3:room3-guava. - LiveData (
LiveData): DaftarkanLiveDataDaoReturnTypeConverterdari artefakandroidx.room3:room3-livedata.
Memverifikasi penghapusan Observer InvalidationTracker
Room 3.0 sepenuhnya menghapus InvalidationTracker.Observer dan metode pendaftaran terkait, seperti addObserver dan removeObserver.
Jika belum beralih ke aliran coroutine di
Fase 1, Anda harus memigrasikan semua Observer penggunaan ke
createFlow:
val userFlow = db.invalidationTracker.createFlow("User").map { _ ->
userDao.getAllUsers()
}