Room 3.0 là một bản cập nhật phiên bản chính, chuyển thư viện này sang Kotlin-first. Thư viện này hỗ trợ Kotlin Multiplatform (KMP), yêu cầu Kotlin Symbol Processing (KSP) và thực thi các coroutine cho các thao tác không đồng bộ.
Để ngăn các vấn đề về khả năng tương thích với các ứng dụng Room 2.x hiện có và các phần phụ thuộc bắc cầu, Room 3.0 nằm trong một gói mới: androidx.room3.
Hướng dẫn này trình bày các bước cần thiết để di chuyển hoạt động triển khai Room 2.x hiện có sang Room 3.0.
Các thay đổi chính trong Room 3.0
Trước khi bắt đầu di chuyển, hãy tìm hiểu những điểm khác biệt chính:
- Gói và cấu phần phần mềm mới: Tất cả các lớp đều nằm trong
androidx.room3. Các cấu phần phần mềm sử dụng tiền tốroom3, chẳng hạn nhưandroidx.room3:room3-runtime. - Chỉ Kotlin và KSP: Room 3.0 không hỗ trợ việc tạo mã Java. Sử dụng KSP thay vì KAPT hoặc trình xử lý chú giải Java. Room 3.0 vẫn hỗ trợ các nguồn Java làm dữ liệu đầu vào.
- Ưu tiên coroutine: Các hàm DAO phải là hàm
suspend, ngoại trừ các loại có thể quan sát.CoroutineContextthay thế các trình thực thi. - Không có SupportSQLite: Các API
SQLiteDriverhỗ trợ Room. Room xoáSupportSQLiteDatabasekhỏi các API cốt lõi. - Thay đổi về API: Các lệnh gọi lại cơ sở dữ liệu và hoạt động di chuyển sử dụng
SQLiteConnectionthay vìSupportSQLiteDatabase. - Trình chuyển đổi cho các loại phản ứng: Các loại phản hồi RxJava, LiveData, Guava và Phân trang yêu cầu bạn đăng ký
@DaoReturnTypeConverters.
Bạn nên di chuyển theo 2 giai đoạn riêng biệt: đầu tiên là chuẩn bị và hiện đại hoá toàn bộ mã nguồn trong Room 2.x, sau đó chuyển sang Room 3.0.
Chuẩn bị và hiện đại hoá trong Room 2.x
Trước khi di chuyển sang Room 3.0, bạn có thể thực hiện hầu hết các công việc hiện đại hoá bằng cách cập nhật lên bản phát hành Room 2.x hiện tại, chẳng hạn như Room 2.8. Room 2.8 hỗ trợ Kotlin Multiplatform (KMP) và bao gồm nhiều API trình điều khiển mà Room 3.0 sử dụng.
Cập nhật lên Room 2.8 trở lên
Cập nhật cấu hình bản dựng để sử dụng bản phát hành Room 2.x hiện tại:
[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" }
Di chuyển từ KAPT sang KSP
Room 3.0 không hỗ trợ trình xử lý chú giải Java hoặc KAPT. Bạn phải sử dụng Kotlin Symbol Processing (KSP). Bạn có thể thực hiện quá trình chuyển đổi này trong khi vẫn sử dụng Room 2.x.
Trong
build.gradle.ktscủa mô-đun, hãy áp dụng trình bổ trợ KSP:plugins { id("com.google.devtools.ksp") version "<ksp_version>" }Đảm bảo rằng phiên bản KSP tương thích với phiên bản Kotlin của bạn.
Thay thế
kapthoặcannotationProcessorbằngkspcho phần phụ thuộc trình biên dịch Room:dependencies { implementation(libs.androidx.room.runtime) ksp(libs.androidx.room.compiler) }
Sử dụng coroutine
Room 3.0 yêu cầu coroutine cho các thao tác không đồng bộ.
- Cập nhật DAO: Trừ phi trả về một loại phản ứng có thể quan sát được, chẳng hạn như
Flowhoặc các loại RxJava, tất cả các hàm DAO phải là hàmsuspend.
// 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>
}
- Nếu bạn đã định cấu hình
RoomDatabasebằngExecutortuỳ chỉnh để thực hiện các thao tác trên cơ sở dữ liệu, hãy di chuyển đếnCoroutineContextbằng cách sử dụngsetQueryCoroutineContexttrên trình tạo:
Room.databaseBuilder<AppDatabase>(context, "db")
.setQueryCoroutineContext(Dispatchers.IO)
.build()
Sử dụng các API trình điều khiển và tránh dùng Support SQLite
Room 3.0 được SQLiteDriver hỗ trợ đầy đủ và không còn hỗ trợ SupportSQLiteDatabase trong các API cốt lõi nữa.
Nếu bạn không gọi setDriver để đặt SQLiteDriver trên trình tạo cơ sở dữ liệu, thì Room 2.8 sẽ hoạt động ở chế độ tương thích, trong đó cả Support SQLite và Driver API đều hoạt động. Chế độ tương thích này cho phép bạn chuyển đổi dần toàn bộ mã nguồn trước khi bật trình điều khiển.
- Chuyển đổi các hoạt động di chuyển: Di chuyển các lớp con
MigrationvàAutoMigrationSpecđể sử dụngSQLiteConnectionthay vì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"
)
}
}
- Chuyển đổi lệnh gọi lại cơ sở dữ liệu: Cập nhật các hoạt động triển khai
RoomDatabase.Callbackđể sử dụngSQLiteConnection:
// 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) {
// ...
}
}
- Chuyển đổi các hàm DAO
@RawQuery: Đối với các hàm được chú thích bằng@RawQuery, hãy dùngRoomRawQuerythay vìSupportSQLiteQuery:
// Before (SupportSQLiteQuery)
@Dao
interface UserDao {
@RawQuery
fun getUser(query: SupportSQLiteQuery): User
}
// After (RoomRawQuery)
@Dao
interface UserDao {
@RawQuery
suspend fun getUser(query: RoomRawQuery): User
}
Bạn có thể tạo một RoomRawQuery trong thời gian chạy:
val query = RoomRawQuery(
sql = "SELECT * FROM User WHERE id = ?",
onBindStatement = { statement ->
statement.bindInt(1, userId)
}
)
- Chuyển đổi API giao dịch: Thay thế các khối
withTransactionvàrunInTransactionchỉ dành cho Android bằngwithWriteTransactionhoặcwithReadTransaction:
// Before (withTransaction)
db.withTransaction {
// perform database operations
}
// After (withWriteTransaction - Room 2.8)
import androidx.room.withWriteTransaction
db.withWriteTransaction {
// perform database operations
}
Nếu cần quyền truy cập trực tiếp ở cấp thấp vào kết nối giao dịch, bạn cũng có thể sử dụng useWriterConnection với immediateTransaction.
- Tránh sử dụng trực tiếp
SupportSQLiteDatabase: Nếu bạn có nhiều mã cũ vẫn yêu cầuSupportSQLiteDatabasevà bạn chưa thể di chuyển mã đó, hãy sử dụng cấu phần phần mềm tương thíchandroidx.room:room-sqlite-wrapper:
dependencies {
implementation("androidx.room:room-sqlite-wrapper:$roomVersion")
}
Sau đó, hãy dùng getSupportWrapper để lấy một SupportSQLiteDatabase từ phiên bản cơ sở dữ liệu Room:
import androidx.room.support.getSupportWrapper
val legacyDb: SupportSQLiteDatabase = roomDatabase.getSupportWrapper()
- Thiết lập trình điều khiển SQLite: Sau khi bạn di chuyển tất cả các cách sử dụng Room API sang driver API, hãy định cấu hình một trình điều khiển, chẳng hạn như
BundledSQLiteDriverhoặcAndroidSQLiteDriver, bằng cách gọisetDrivertrong trình tạoRoomDatabase:
import androidx.sqlite.driver.bundled.BundledSQLiteDriver
val db = Room.databaseBuilder<AppDatabase>(context, "db")
.setDriver(BundledSQLiteDriver())
.build()
Sử dụng tính năng theo dõi việc vô hiệu hoá dựa trên luồng
Room 2.8 giới thiệu API InvalidationTracker.createFlow. Sử dụng API này để di chuyển khỏi các quy trình triển khai InvalidationTracker.Observer cũ trong khi vẫn dùng Room 2.x. Thao tác này sẽ chuẩn bị cơ sở mã của bạn cho Room 3.0, phiên bản này sẽ xoá hoàn toàn 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()
}
Di chuyển sang Room 3.0
Sau khi hiện đại hoá ứng dụng trên Room 2.x, việc chuyển đổi sang Room 3.0 sẽ liên quan đến việc cập nhật các phần phụ thuộc, lượt nhập gói và lệnh gọi lại cơ sở dữ liệu.
Cập nhật phần phụ thuộc và nhập gói
- Trong cấu hình bản dựng, hãy thay thế các phần phụ thuộc
androidx.roombằngandroidx.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" }
- Cập nhật khối phần phụ thuộc:
dependencies {
implementation(libs.androidx.room3.runtime)
ksp(libs.androidx.room3.compiler)
}
- Cập nhật các gói nhập. Thay thế
import androidx.room.*bằngimport androidx.room3.*.
Cập nhật API trình chuyển đổi loại
Room 3.0 đổi tên các API trình chuyển đổi loại để làm rõ cách sử dụng của chúng trong việc chuyển đổi các giá trị cột và tránh nhầm lẫn với các trình chuyển đổi loại trả về DAO.
Cập nhật các chú giải và hàm sau đây trong toàn bộ mã nguồn của bạn:
- Đổi tên
@TypeConverterthành@ColumnTypeConverter. - Đổi tên
@TypeConvertersthành@ColumnTypeConverters. - Đổi tên
@ProvidedTypeConverterthành@ProvidedColumnTypeConverter. - Đổi tên
RoomDatabase.Builder.addTypeConverterthànhaddColumnTypeConverter.
Ví dụ:
// 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()
Cập nhật lệnh gọi lại thành các hàm tạm ngưng
Trong Room 3.0, các lệnh gọi lại và hoạt động di chuyển cơ sở dữ liệu sử dụng SQLiteConnection và là các hàm suspend.
- Cập nhật các lớp học
Migrationtheo cách thủ công:
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"
)
}
}
- Cập nhật các hoạt động triển khai
RoomDatabase.Callback:
val callback = object : RoomDatabase.Callback() {
override suspend fun onCreate(connection: SQLiteConnection) {
// ...
}
}
Đăng ký trình chuyển đổi kiểu dữ liệu trả về DAO
Trong Room 3.0, các kiểu dữ liệu trả về phản ứng (chẳng hạn như RxJava, LiveData, Guava và Paging) yêu cầu bạn đăng ký các trình chuyển đổi kiểu dữ liệu trả về DAO bằng @DaoReturnTypeConverters.
import androidx.room3.paging.PagingSourceDaoReturnTypeConverter
@Dao
@DaoReturnTypeConverters(PagingSourceDaoReturnTypeConverter::class)
interface UserDao {
@Query("SELECT * FROM User")
fun getAllPaginated(): PagingSource<Int, User>
}
- Phân trang (
PagingSource): Đăng kýPagingSourceDaoReturnTypeConvertertừ cấu phần phần mềmandroidx.room3:room3-paging. - RxJava (
Observable,Flowable,Single,Maybe,Completable): Đăng kýRxDaoReturnTypeConverterstừ cấu phần phần mềmandroidx.room3:room3-rxjava3. - Guava (
ListenableFuture): Đăng kýGuavaDaoReturnTypeConvertertừ cấu phần phần mềmandroidx.room3:room3-guava. - LiveData (
LiveData): Đăng kýLiveDataDaoReturnTypeConvertertừ cấu phần phần mềmandroidx.room3:room3-livedata.
Xác minh việc xoá đối tượng theo dõi InvalidationTracker
Room 3.0 xoá hoàn toàn InvalidationTracker.Observer và các phương thức đăng ký liên quan, chẳng hạn như addObserver và removeObserver.
Nếu chưa chuyển sang luồng coroutine trong Giai đoạn 1, bạn phải di chuyển tất cả các cách sử dụng Observer sang createFlow:
val userFlow = db.invalidationTracker.createFlow("User").map { _ ->
userDao.getAllUsers()
}