مهاجرت از اتاق ۲.x به اتاق ۳.۰

Room 3.0 is a major version update that transitions the library to be Kotlin-first. It supports Kotlin Multiplatform (KMP), requires Kotlin Symbol Processing (KSP), and enforces coroutines for asynchronous operations.

برای جلوگیری از مشکلات سازگاری با برنامه‌های Room 2.x موجود و وابستگی‌های انتقالی، Room 3.0 در یک بسته جدید قرار دارد: androidx.room3 .

این راهنما مراحل لازم برای انتقال پیاده‌سازی Room 2.x موجود شما به Room 3.0 را شرح می‌دهد.

تغییرات کلیدی در اتاق ۳.۰

قبل از شروع مهاجرت، با تفاوت‌های اصلی آشنا شوید:

  • بسته و مصنوعات جدید : همه کلاس‌ها در androidx.room3 قرار دارند. مصنوعات از پیشوند room3 استفاده می‌کنند، مانند androidx.room3:room3-runtime .
  • فقط Kotlin و KSP : Room 3.0 از تولید کد جاوا پشتیبانی نمی‌کند. به جای KAPT یا پردازنده‌های حاشیه‌نویسی جاوا از KSP استفاده کنید. Room 3.0 همچنان از منابع جاوا به عنوان ورودی پشتیبانی می‌کند.
  • Coroutine first : توابع DAO باید توابع suspend باشند، به جز انواع observable. CoroutineContext جایگزین executorها می‌شود.
  • بدون SupportSQLite : رابط‌های برنامه‌نویسی SQLiteDriver Room را پشتیبانی می‌کنند. Room، SupportSQLiteDatabase از رابط‌های برنامه‌نویسی اصلی حذف می‌کند.
  • تغییرات API : مهاجرت‌ها و فراخوانی‌های پایگاه داده به جای SupportSQLiteDatabase از SQLiteConnection استفاده می‌کنند.
  • مبدل‌ها برای انواع واکنشی : انواع بازگشتی RxJava، LiveData، Guava و Paging نیاز دارند که شما @DaoReturnTypeConverters را ثبت کنید.

ما توصیه می‌کنیم مهاجرت را در دو مرحله مجزا انجام دهید: ابتدا آماده‌سازی و مدرن‌سازی پایگاه کد خود در Room 2.x و سپس انتقال به Room 3.0.


آماده‌سازی و نوسازی در اتاق ۲.x

Before migrating to Room 3.0, you can perform most modernization work by updating to the current Room 2.x release, like Room 2.8. Room 2.8 supports Kotlin Multiplatform, or KMP, and includes many driver APIs that Room 3.0 uses.

به اتاق ۲.۸ و بالاتر ارتقا دهید

پیکربندی ساخت خود را برای استفاده از نسخه فعلی 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

روم ۳.۰ از پردازنده‌های حاشیه‌نویسی جاوا یا KAPT پشتیبانی نمی‌کند. شما باید از پردازش نمادهای کاتلین (KSP) استفاده کنید. می‌توانید این انتقال را در حالی که هنوز در روم ۲.x هستید، انجام دهید.

  1. در build.gradle.kts ماژول خود، افزونه KSP را اعمال کنید:

    plugins {
        id("com.google.devtools.ksp") version "<ksp_version>"
    }
    

    مطمئن شوید که نسخه KSP با نسخه کاتلین شما سازگار است.

  2. برای وابستگی کامپایلر Room kapt یا annotationProcessor را با ksp جایگزین کنید:

    dependencies {
        implementation(libs.androidx.room.runtime)
        ksp(libs.androidx.room.compiler)
    }
    

کوروتین‌ها را بپذیرید

اتاق ۳.۰ برای عملیات ناهمزمان به کوروتین نیاز دارد.

  • به‌روزرسانی DAOهای خود: مگر اینکه یک نوع واکنشی قابل مشاهده، مانند انواع Flow یا RxJava، را برگردانند، تمام توابع DAO باید توابع suspend باشند.
// 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 سفارشی برای انجام عملیات پایگاه داده پیکربندی کرده‌اید، با استفاده از setQueryCoroutineContext در سازنده، به CoroutineContext مهاجرت کنید:
Room.databaseBuilder<AppDatabase>(context, "db")
    .setQueryCoroutineContext(Dispatchers.IO)
    .build()

API های درایور را اتخاذ کنید و از پشتیبانی SQLite خودداری کنید

روم ۳.۰ به طور کامل توسط SQLiteDriver پشتیبانی می‌شود و دیگر SupportSQLiteDatabase در APIهای اصلی خود پشتیبانی نمی‌کند.

If you don't call setDriver to set a SQLiteDriver on your database builder, Room 2.8 operates in a compatibility mode where both Support SQLite and Driver APIs function. This compatibility mode lets you incrementally convert your codebase before enabling the driver.

  • تبدیل مهاجرت‌ها : زیرکلاس‌های Migration و AutoMigrationSpec خود را طوری مهاجرت دهید که به جای SupportSQLiteDatabase از SQLiteConnection استفاده کنند.
// 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 حاشیه‌نویسی شده‌اند، به جای SupportSQLiteQuery از RoomRawQuery استفاده کنید:
// 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 که فقط برای اندروید هستند را با 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 نیز استفاده کنید.

  • Avoid direct usage of SupportSQLiteDatabase : If you have extensive legacy code that still requires SupportSQLiteDatabase and you can't migrate it yet, use the androidx.room:room-sqlite-wrapper compatibility artifact:
dependencies {
    implementation("androidx.room:room-sqlite-wrapper:$roomVersion")
}

و سپس، getSupportWrapper برای دریافت SupportSQLiteDatabase از نمونه پایگاه داده Room خود استفاده کنید:

import androidx.room.support.getSupportWrapper

val legacyDb: SupportSQLiteDatabase = roomDatabase.getSupportWrapper()
  • Set the SQLite driver : After you migrate all Room API usages to driver APIs, configure a driver, like BundledSQLiteDriver or AndroidSQLiteDriver , by calling setDriver in your RoomDatabase builder:
import androidx.sqlite.driver.bundled.BundledSQLiteDriver

val db = Room.databaseBuilder<AppDatabase>(context, "db")
    .setDriver(BundledSQLiteDriver())
    .build()

ردیابی ابطال مبتنی بر جریان را اتخاذ کنید

Room 2.8 introduces the InvalidationTracker.createFlow API. Use this API to migrate away from legacy InvalidationTracker.Observer implementations while still on Room 2.x. This prepares your codebase for Room 3.0, which completely removes 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()
}

مهاجرت به اتاق ۳.۰

زمانی که برنامه خود را در 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 خود را به‌روزرسانی کنید. import androidx.room.* ‎ را با import androidx.room3.* ‎ جایگزین کنید.

به‌روزرسانی APIهای مبدل نوع

اتاق ۳.۰ نام 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()

به‌روزرسانی فراخوانی‌های برگشتی برای تعلیق توابع

در اتاق ۳.۰، فراخوانی‌های پایگاه داده و مهاجرت‌ها از 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 رجیستر

در اتاق ۳.۰، انواع برگشتی واکنشی، مانند 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>
}
  • صفحه‌بندی ( 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 ) : از روی مصنوع androidx.room3:room3-livedata LiveDataDaoReturnTypeConverter را ثبت کنید.

تأیید حذف ناظر InvalidationTracker

اتاق ۳.۰ به طور کامل InvalidationTracker.Observer و متدهای ثبت نام مرتبط، مانند addObserver و removeObserver حذف می‌کند.

اگر در فاز ۱ به جریان‌های کوروتین منتقل نشده‌اید، باید تمام کاربردهای Observer را به createFlow منتقل کنید:

val userFlow = db.invalidationTracker.createFlow("User").map { _ ->
    userDao.getAllUsers()
}