Room یک کتابخانه برای کار با SQLite در اندروید است که بخشی از Jetpack میباشد. این کتابخانه یک لایه انتزاع بر روی SQLite خام فراهم میکند و ایجاد جداول، اجرای پرسوجوها و تبدیل دادهها به اشیاء Kotlin و Java را خودکار میسازد. به گفته Android Developers، Room پرسوجوهای SQL را در مرحله ساخت کامپایل میکند و صحت نحو و ارتباطات بین Entity و جداول را بررسی مینماید.
نکات کلیدی
Room — یک کتابخانه ماندگاری از مجموعه Android Jetpack است که نگاشت شیء-رابطهای برای SQLite فراهم میکند. Room سه مشکل اصلی SQLite خام را حل میکند: نوشتن حجم زیادی از کد boilerplate برای ایجاد جداول، عدم بررسی پرسوجوهای SQL در مرحله کامپایل و تبدیل دستی Cursor به اشیاء.
این کتابخانه از کامپایلر نشانهگذاری (kapt یا KSP) استفاده میکند که پیادهسازی کلاسهای انتزاعی RoomDatabase و DAO را در مرحله ساخت تولید میکند. این تضمین میکند که خطاهای نحوی در SQL و ناسازگاری نوعها قبل از اجرای برنامه کشف میشوند، نه در زمان اجرا پس از انتشار در Google Play.
بر اساس Google I/O 2023، Room در 68% از برنامههای اندرویدی که با دادههای محلی کار میکنند استفاده میشود. این استاندارد ذخیرهسازی داده در دستگاه است که توسط Google برای همه پروژههای جدید توصیه میشود — به جای SQLiteOpenHelper و ContentProvider منسوخ شده.
Room را در پروژههایی پیادهسازی کنید که نیاز به کش محلی دادههای سرور، حالت آفلاین یا ذخیرهسازی دادههای ساختاریافته کاربر با امکان پرسوجوهای پیچیده SQL دارند.
Room بخشی از Android Jetpack است و به طور رسمی توسط Google برای همه پروژههای جدید که با دادههای محلی کار میکنند توصیه میشود. برخلاف Realm یا ObjectBox، Room از SQLite بومی استفاده میکند که سازگاری با هر ابزار شخص ثالث برای کار با پایگاه داده — از DB Browser تا DataGrip — را تضمین میکند. توسعهدهنده میتواند فایل .db برنامه را باز کرده و مستقیماً پرسوجوهای SQL را اجرا کند که دیباگ و تحلیل دادهها را در فرآیند توسعه آسانتر میسازد.
Entity — یک کلاس داده است که با @Entity نشانهگذاری شده و Room آن را به یک جدول پایگاه داده تبدیل میکند. هر فیلد کلاس به یک ستون جدول تبدیل میشود و هر نمونه به یک سطر. Room از بازتاب برای دسترسی به فیلدها استفاده میکند، بنابراین نشانهگذاری @PrimaryKey برای شناسه اجباری مورد نیاز است.
نشانهگذاری @Entity به Room اعلام میکند که کلاس یک جدول است. پارامتر tableName نام جدول را تعیین میکند اگر با نام کلاس متفاوت باشد. @PrimaryKey کلید اصلی را با امکان تولید خودکار از طریق autoGenerate = true تعریف میکند.
@Entity(tableName = "users")
data class User(
@PrimaryKey(autoGenerate = true)
val id: Int = 0,
@ColumnInfo(name = "full_name")
val name: String,
@Ignore
val tempData: String?
)
@ColumnInfo نام ستون در جدول را تعیین میکند اگر با نام فیلد Kotlin متفاوت باشد. @Ignore فیلد را از جدول حذف میکند — در پایگاه داده ذخیره نخواهد شد. @ForeignKey کلیدهای خارجی را برای روابط بین جداول با عملیات آبشاری هنگام حذف یا بهروزرسانی توصیف میکند.
Room از اشیاء تو در تو از طریق نشانهگذاری @Embedded پشتیبانی میکند. فیلدهای کلاس تو در تو با یک پیشوند برای جلوگیری از تداخل نام به ستونهای جدول والد باز میشوند. به عنوان مثال، کلاس Address با فیلدهای city و street که در User جاسازی شده است، ستونهای address_city و address_street را در جدول users ایجاد میکند و نیاز به ایجاد جداول جداگانه برای اشیاء مقدار ساده را از بین میبرد.
Room فقط از انواع اولیه و بستهبندیهای آنها پشتیبانی میکند. برای ذخیره لیستها، Date یا انواع سفارشی از @TypeConverter استفاده میشود — متدهای استاتیک تبدیل بین نوع سفارشی و نوع اولیه SQLite، به عنوان مثال بین List و رشته JSON.
DAO (Data Access Object) — یک رابط یا کلاس انتزاعی است که با @Dao نشانهگذاری شده و شامل متدهایی برای دسترسی به دادهها است. هر متد با یک عملیات SQL نشانهگذاری میشود: @Insert، @Update، @Delete یا @Query با یک پرسوجوی SQL صریح.
نشانهگذاری @Query یک رشته SQL میگیرد که توسط Room در مرحله کامپایل از نظر صحت نحو و مطابقت نام ستونها با فیلدهای Entity بررسی میشود. Room از پرسوجوهای پارامتری شده از طریق نحو :paramName پشتیبانی میکند.
@Dao
interface UserDao {
@Query("SELECT * FROM users WHERE id = :userId")
suspend fun getUserById(userId: Int): User?
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertUser(user: User)
@Query("SELECT * FROM users ORDER BY name ASC")
fun getAllUsers(): Flow<List<User>>
}
@Insert از استراتژیهای OnConflictStrategy برای مدیریت تداخلها هنگام درج رکوردهای تکراری پشتیبانی میکند. Flow به عنوان نوع بازگشتی بهروزرسانی واکنشگرای رابط کاربری را با هر تغییر داده در جدول تضمین میکند — اشتراک به طور خودکار با هر INSERT، UPDATE یا DELETE مجدداً راهاندازی میشود.
نشانهگذاری @Transaction اجرای اتمی چندین عملیات را در یک بلوک تراکنشی تضمین میکند. Room پایگاه داده را در طول اجرا قفل میکند و از شرایط مسابقه در دسترسی همزمان از چندین رشته جلوگیری میکند.
RoomDatabase — یک کلاس انتزاعی است که Entity و DAO را در یک نقطه دسترسی واحد به پایگاه داده ترکیب میکند. از طریق Room.databaseBuilder با مشخص کردن نسخه طرح و لیست کلاسهای Entity ایجاد میشود. توصیه میشود نمونه پایگاه داده به عنوان singleton از طریق lazy-delegate برای جلوگیری از اتصالات چندگانه ایجاد شود.
مهاجرت در Room — یک کلاس Migration است که اسکریپت SQL را برای انتقال از نسخه قدیمی طرح به جدید توصیف میکند. اگر در تغییر طرح مهاجرتی ارائه نشود، Room IllegalStateException پرتاب میکند. این از از دست رفتن تصادفی دادههای کاربر در هنگام بهروزرسانی برنامه محافظت میکند.
val migration_1_2 = object : Migration(1, 2) {
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL("ALTER TABLE users ADD COLUMN age INTEGER NOT NULL DEFAULT 0")
}
}
val db = Room.databaseBuilder(
getApplication(),
AppDatabase::class.java,
"app_database"
).addMigrations(migration_1_2)
.build()
برای توسعه میتوان از fallbackToDestructiveMigration استفاده کرد که پایگاه داده قدیمی را حذف و در صورت عدم تطابق نسخهها جدید ایجاد میکند. این حالت فقط برای دیباگ در نظر گرفته شده است — در نسخههای تولیدی حتماً مهاجرتها نوشته میشوند.
برای آزمایش پایگاه داده، Room یک کلاس ویژه Room.inMemoryTestBuilder ارائه میدهد که پایگاه داده را در حافظه RAM بدون ذخیره روی دیسک ایجاد میکند. پس از اتمام هر تست، پایگاه داده به طور خودکار نابود میشود که جداسازی کامل سناریوهای تست را تضمین میکند. همراه با کتابخانه android-arch-core-testing، توسعهدهنده میتواند چرخه حیات پایگاه داده را مدیریت کرده و صحت مهاجرتها را بدون نیاز به پاک کردن دستی حالت بررسی کند.
عملکرد Room مستقیماً به ساختار پرسوجوها و ایندکسها بستگی دارد. برای تحلیل پرسوجوهای کند، Room پرچم enableQueryCallback را ارائه میدهد که تمام پرسوجوهای SQL را با زمان اجرا ثبت میکند. توسعهدهنده میتواند از این لاگ برای یافتن پرسوجوهایی که بیش از 100 میلیثانیه طول میکشند استفاده کرده و آنها را با افزودن ایندکسهای ترکیبی از طریق نشانهگذاری @Index در @Entity یا بازنویسی زیرپرسوجوها به JOINهای مستقیم با استفاده از @Relation بهینهسازی کند.
Room همچنین از رمزنگاری پایگاه داده از طریق SQLCipher پشتیبانی میکند. اتصال کتابخانه net.zetetic:android-database-sqlcipher و استفاده از SupportFactory به جای استاندارد، رمزنگاری شفاف تمام دادهها روی دیسک را بدون تغییر پرسوجوهای DAO و ساختار Entity تضمین میکند. این برای برنامههایی که با دادههای شخصی کاربران کار میکنند ضروری است و با الزامات GDPR و قانون 152-FZ روسیه در مورد حفاظت از دادههای شخصی مطابقت دارد. رمز عبور رمزنگاری میتواند در Android Keystore برای محافظت در برابر استخراج از طریق ابزارهای موجود در دستگاههای روت شده ذخیره شود.
Room از نسخه 2.1 به صورت بومی از Kotlin Coroutines پشتیبانی میکند. متدهای DAO میتوانند توابع suspend باشند که پرسوجوها را در پسزمینه بدون مسدود کردن رشته اصلی اجرا میکنند. Room به طور خودکار dispatcherها را مدیریت میکند و از Dispatchers.IO برای پرسوجوهای خواندن و نوشتن استفاده میکند.
برای پرسوجوهای واکنشگرا، Room Flow را برمیگرداند — یک جریان داده سرد که با هر تغییر در جدول مرتبط مقدار جدیدی منتشر میکند. ViewModel از طریق stateIn یا collect در Flow مشترک میشود و بهروزرسانی خودکار رابط کاربری را بدون اطلاعرسانی دستی آداپتور تضمین میکند.
Room همچنین از Paging 3 از طریق پیادهسازی ویژه PagingSource پشتیبانی میکند که دادهها را صفحهبندی شده از SQLite بارگیری میکند. این برای لیستهای بزرگ با هزاران رکورد مؤثر است: Paging 3 فقط سطرهای قابل مشاهده روی صفحه را بارگیری میکند و آنها را با تغییرات در پایگاه داده به طور خودکار بهروزرسانی میکند.
از Paging 3 با Room هنگام نمایش فید خبری، لاگ عملیات یا لیست محصولات با امکان دسترسی آفلاین و اسکرول بینهایت استفاده کنید.
سؤالات متداول
Room ایجاد جداول، تبدیل Cursor به اشیاء و بررسی SQL را در مرحله کامپایل خودکار میکند. SQLiteOpenHelper نیاز به نوشتن دستی طرح، پردازش Cursor دارد و قبل از اجرای برنامه پرسوجوها را بررسی نمیکند که خطر خطا را افزایش میدهد.
بله، در تغییر Entity (افزودن/حذف فیلد، تغییر نوع) مهاجرت مورد نیاز است. بدون آن Room هنگام راهاندازی IllegalStateException پرتاب میکند. برای توسعه میتوان fallbackToDestructiveMigration را فعال کرد، اما در نسخه نهایی اسکریپتهای مهاجرت صحیح الزامی است.
Room از @ForeignKey برای عملیات آبشاری و @Relation برای اشیاء تو در تو پشتیبانی میکند. برای پرسوجوهای JOIN پیچیده از نشانهگذاری @Transaction با @Query استفاده میشود که POJO با موجودیتهای تو در تو از طریق @Embedded و @Relation برمیگرداند.
بله، Room کاملاً با Java سازگار است. به جای توابع suspend از LiveData یا RxJava Observable و به جای Flow از LiveData استفاده میشود. Room با Java از تمام نشانهگذاریهای یکسان پشتیبانی میکند، اما برای عملیات ناهمگام به کد boilerplate بیشتری نیاز دارد.
Room از رمزنگاری از طریق SQLCipher توسط Zetetic پشتیبانی میکند. به جای Room.databaseBuilder از SupportFactory کتابخانه net.zetetic:android-database-sqlcipher استفاده کنید و رمز عبور رمزنگاری را ارسال کنید. تمام دادههای روی دیسک به صورت شفاف برای پرسوجوهای DAO رمزنگاری میشوند.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.