Room 3.0 هو تحديث رئيسي للإصدار ينقل المكتبة لتكون متوافقة مع Kotlin أولاً. وهي تتوافق مع Kotlin Multiplatform (KMP)، وتتطلّب Kotlin Symbol Processing (KSP)، وتفرض استخدام الكوروتين للعمليات غير المتزامنة.
لمنع حدوث مشاكل في التوافق مع تطبيقات Room 2.x الحالية والتبعيات المتعدية، يتوفّر Room 3.0 في حزمة جديدة: androidx.room3.
يحدّد هذا الدليل الخطوات المطلوبة لنقل عملية تنفيذ Room 2.x الحالية إلى Room 3.0.
التغييرات الرئيسية في Room 3.0
قبل بدء عملية النقل، تعرَّف على الاختلافات الرئيسية:
- حزمة وعناصر جديدة: تتوفّر جميع الفئات في
androidx.room3. تستخدم العناصر البادئةroom3، مثلandroidx.room3:room3-runtime. - Kotlin وKSP فقط: لا يتوافق Room 3.0 مع إنشاء رموز Java البرمجية. استخدِم KSP بدلاً من KAPT أو معالِجات تعليقات Java التوضيحية. لا يزال Room 3.0 يتوافق مع مصادر Java كمدخلات.
- الكوروتين أولاً: يجب أن تكون دوالّ DAO دوالّ
suspend، باستثناء الأنواع القابلة للمراقبة. يحلّCoroutineContextمحلّ المنفّذين. - لا تتوفّر SupportSQLite: تستند واجهات برمجة التطبيقات
SQLiteDriverإلى Room. يزيل Room السمةSupportSQLiteDatabaseمن واجهات برمجة التطبيقات الأساسية. - تغييرات واجهة برمجة التطبيقات: تستخدِم عمليات النقل وعمليات معاودة الاتصال بقاعدة البيانات
SQLiteConnectionبدلاً منSupportSQLiteDatabase. - المحوّلات للأنواع التفاعلية: تتطلّب أنواع الإرجاع RxJava وLiveData وGuava وPaging
تسجيل
@DaoReturnTypeConverters.
ننصحك بإجراء عملية النقل على مرحلتَين مختلفتَين: أولاً، إعداد قاعدة الرموز البرمجية وتحديثها في Room 2.x، ثم التبديل إلى Room 3.0.
الإعداد والتحديث في Room 2.x
قبل النقل إلى Room 3.0، يمكنك إجراء معظم أعمال التحديث من خلال الترقية إلى إصدار Room 2.x الحالي، مثل Room 2.8. يتوافق Room 2.8 مع Kotlin Multiplatform أو KMP، ويتضمّن العديد من واجهات برمجة التطبيقات الخاصة ببرامج التشغيل التي يستخدمها Room 3.0.
الترقية إلى Room 2.8 والإصدارات الأحدث
عدِّل إعدادات التصميم لاستخدام إصدار 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
لا يتوافق Room 3.0 مع معالِجات تعليقات Java التوضيحية أو KAPT. عليك استخدام Kotlin Symbol Processing (KSP). يمكنك إجراء عملية النقل هذه أثناء استخدام Room 2.x.
في ملف
build.gradle.ktsالخاص بالوحدة، طبِّق المكوّن الإضافي KSP:plugins { id("com.google.devtools.ksp") version "<ksp_version>" }تأكَّد من توافق إصدار KSP مع إصدار Kotlin.
استبدِل
kaptأوannotationProcessorبـkspلتبعّية برنامج تجميع Room:dependencies { implementation(libs.androidx.room.runtime) ksp(libs.androidx.room.compiler) }
استخدِم الكوروتين
يتطلّب Room 3.0 استخدام الكوروتين للعمليات غير المتزامنة.
- عدِّل دوالّ DAO: يجب أن تكون جميع دوالّ DAO دوالّ
suspend، ما لم تعرض نوعًا تفاعليًا قابلاً للمراقبة، مثلFlowأو أنواع 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>
}
- إذا سبق لك ضبط
RoomDatabaseباستخدامExecutorمخصّص لإجراء عمليات قاعدة البيانات، يمكنك النقل إلىCoroutineContextباستخدامsetQueryCoroutineContextفي أداة الإنشاء:
Room.databaseBuilder<AppDatabase>(context, "db")
.setQueryCoroutineContext(Dispatchers.IO)
.build()
استخدِم واجهات برمجة التطبيقات الخاصة ببرامج التشغيل وتجنَّب Support SQLite
يستند Room 3.0 بالكامل إلى SQLiteDriver ولم يعُد يتوافق مع SupportSQLiteDatabase في واجهات برمجة التطبيقات الأساسية.
إذا لم تستدعِ setDriver لضبط SQLiteDriver في أداة إنشاء قاعدة البيانات، سيعمل Room 2.8 في وضع التوافق الذي تعمل فيه كل من واجهات برمجة التطبيقات Support SQLite وDriver. يتيح لك وضع التوافق هذا تحويل قاعدة الرموز البرمجية تدريجيًا قبل تفعيل برنامج التشغيل.
- تحويل عمليات النقل: يمكنك نقل الفئتَين الفرعيتَين
MigrationوAutoMigrationSpecلاستخدامSQLiteConnectionبدلاً من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"
)
}
}
- تحويل عمليات معاودة الاتصال بقاعدة البيانات: يمكنك تعديل عمليات تنفيذ
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، استخدِمRoomRawQueryبدلاً منSupportSQLiteQuery:
// 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)
}
)
- تحويل واجهات برمجة التطبيقات الخاصة بالمعاملات: استبدِل الحزمتَين المخصّصتَين لنظام Android
withTransactionوrunInTransactionبـuseWriterConnectionوimmediateTransaction:
// 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
}
}
- تجنَّب الاستخدام المباشر لـ
SupportSQLiteDatabase: إذا كان لديك رمز قديم واسع النطاق لا يزال يتطلّبSupportSQLiteDatabaseولا يمكنك نقله بعد، استخدِم عنصر التوافقandroidx.room:room-sqlite-wrapper
dependencies {
implementation("androidx.room:room-sqlite-wrapper:$roomVersion")
}
بعد ذلك، استخدِم getSupportWrapper للحصول على SupportSQLiteDatabase من مثيل قاعدة بيانات Room:
import androidx.room.support.getSupportWrapper
val legacyDb: SupportSQLiteDatabase = roomDatabase.getSupportWrapper()
- ضبط برنامج تشغيل SQLite: بعد نقل جميع استخدامات Room API إلى واجهات برمجة التطبيقات الخاصة ببرامج التشغيل
، يمكنك ضبط برنامج تشغيل، مثل
BundledSQLiteDriverأوAndroidSQLiteDriver، من خلال استدعاءsetDriverفي أداة إنشاءRoomDatabase:
import androidx.sqlite.driver.bundled.BundledSQLiteDriver
val db = Room.databaseBuilder<AppDatabase>(context, "db")
.setDriver(BundledSQLiteDriver())
.build()
استخدِم ميزة تتبُّع الإبطال المستندة إلى التدفق
يقدّم Room 2.8 واجهة برمجة التطبيقات InvalidationTracker.createFlow. استخدِم واجهة برمجة التطبيقات هذه لنقل عمليات تنفيذ InvalidationTracker.Observer القديمة أثناء استخدام Room 2.x. يؤدي ذلك إلى إعداد قاعدة الرموز البرمجية لـ Room 3.0 الذي يزيل 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 3.0
بعد تحديث تطبيقك على 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 androidx.room.*بـimport androidx.room3.*.
تعديل واجهات برمجة التطبيقات الخاصة بمحوّلات الأنواع
تعيد Room 3.0 تسمية واجهات برمجة التطبيقات الخاصة بمحوّلات الأنواع لتوضيح استخدامها لتحويل قيم الأعمدة وتجنُّب الخلط بينها وبين محوّلات أنواع الإرجاع في 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()
تعديل عمليات معاودة الاتصال لاستخدام دوالّ `suspend`
في Room 3.0، تستخدِم عمليات معاودة الاتصال بقاعدة البيانات وعمليات النقل 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
في Room 3.0، تتطلّب أنواع القيمة التي تم إرجاعها التفاعلية، مثل 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>
}
- Paging (
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): سجِّلLiveDataDaoReturnTypeConverterمن العنصرandroidx.room3:room3-livedata.
التحقّق من إزالة `InvalidationTracker.Observer`
يزيل Room 3.0 بالكامل InvalidationTracker.Observer وطُرق التسجيل ذات الصلة، مثل addObserver وremoveObserver.
إذا لم تنتقل بعد إلى تدفقات الكوروتين في
المرحلة 1، عليك نقل جميع استخدامات Observer إلى
createFlow:
val userFlow = db.invalidationTracker.createFlow("User").map { _ ->
userDao.getAllUsers()
}