
الغرفة عبارة عن مكتبة تشكل جزءًا من المكونات المعمارية لنظام Android. فإنه يسهل العمل مع كائنات SQLiteDatabase
في التطبيق ، مما يقلل من كمية التعليمات البرمجية القياسية والتحقق من استعلامات SQL في وقت الترجمة.
هل لديك بالفعل مشروع Android يستخدم SQLite لتخزين البيانات؟ إذا كان الأمر كذلك ، فيمكنك ترحيلها إلى Room. دعونا نرى كيفية اتخاذ مشروع موجود وإعادة تشكيله لاستخدام الغرفة في 7 خطوات بسيطة.
TL ؛ DR: تحديث تبعيات gradle ، إنشاء كياناتك ، DAO وقاعدة البيانات ، واستبدال مكالمات SQLiteDatabase
بمكالمات طريقة DAO ، واختبار كل ما قمت بإنشائه أو تعديله ، وحذف الفئات غير المستخدمة. هذا كل شئ!
في نموذج طلبنا للترحيل ، نعمل مع كائنات من النوع User
. استخدمنا نكهات المنتج لإظهار العديد من التطبيقات على مستوى البيانات:
- sqlite - يستخدم
SQLiteOpenHelper
واجهات SQLite التقليدية. - room - يستبدل التنفيذ بـ Room ويوفر الترحيل.
يستخدم كل خيار طبقة واجهة المستخدم نفسها ، والتي تعمل مع فئة UserRepository
بفضل نمط MVP.
في متغير sqlite ، سترى الكثير من التعليمات البرمجية التي غالباً ما تكون مكررة وتستخدم قاعدة البيانات في UsersDbHelper
و LocalUserDataSource
. يتم إنشاء الاستعلامات باستخدام ContentValues
، ContentValues
البيانات التي يتم إرجاعها بواسطة كائنات Cursor
عمودًا تلو الآخر. كل هذا الرمز يساهم في ظهور الأخطاء الضمنية. على سبيل المثال ، يمكنك تخطي إضافة عمود إلى استعلام أو تجميع كائن من قاعدة بيانات بشكل غير صحيح.
دعونا نرى كيف يحسن الغرفة كودنا. في البداية ، نحن ببساطة نسخ الطبقات من البديل sqlite وتغييرها تدريجيا.
الخطوة 1. تحديث تبعيات gradle
تتوفر تبعيات الغرفة من خلال مستودع Google Maven الجديد. فقط أضفه إلى قائمة المستودعات في ملف build.gradle
الرئيسي:
allprojects { repositories { google() jcenter() } }
تحديد نسخة من مكتبة الغرفة في نفس الملف. أثناء وجوده في إصدار alpha ، لكن ترقب تحديثات الإصدار على صفحات مطور البرامج :
ext { roomVersion = '2.1.0-alpha03' }
في app/build.gradle
أضف تبعيات لـ Room:
dependencies { implementation “android.arch.persistence.room:runtime:$rootProject.roomVersion” annotationProcessor “android.arch.persistence.room:compiler:$rootProject.roomVersion” androidTestImplementation “android.arch.persistence.room:testing:$rootProject.roomVersion” }
للترحيل إلى Room ، نحتاج إلى زيادة إصدار قاعدة البيانات ، وحفظ بيانات المستخدم ، نحتاج إلى تطبيق فئة الترحيل . لاختبار الترحيل ، نحتاج إلى تصدير المخطط. للقيام بذلك ، أضف الكود التالي إلى app/build.gradle
:
android { defaultConfig { ... // used by Room, to test migrations javaCompileOptions { annotationProcessorOptions { arguments = ["room.schemaLocation": "$projectDir/schemas".toString()] } } } // used by Room, to test migrations sourceSets { androidTest.assets.srcDirs += files("$projectDir/schemas".toString()) } ...
الخطوة 2. تحديث فئات النموذج للكيانات
تنشئ الغرفة جدولًا لكل فئة تحمل علامة Entity . تتوافق الحقول في الفصل مع الأعمدة الموجودة في الجدول. وبالتالي ، فإن فئات الكيانات ، كقاعدة عامة ، هي فئات صغيرة من النماذج التي لا تحتوي على أي منطق. تمثل فئة User
نموذجًا للبيانات في قاعدة البيانات. لذلك ، دعونا نقوم بتحديثه لإخبار Room بأنه يجب عليه إنشاء جدول بناءً على هذه الفئة:
- قم
@Entity
الفئة مع @Entity
واستخدم خاصية tableName
لتعيين اسم الجدول. - قم بتعيين المفتاح الأساسي عن طريق إضافة تعليق توضيحي
@PrimaryKey
إلى الحقول الصحيحة - في حالتنا ، هذا هو معرف المستخدم. - حدد اسم العمود لحقول الفئة باستخدام التعليق التوضيحي
@ColumnInfo(name = "column_name")
. يمكنك تخطي هذه الخطوة إذا تم بالفعل تسمية الحقول الخاصة بك كما يجب تسمية العمود. - إذا كان هناك العديد من
@Ignore
في الفصل ، فأضف @Ignore
على @Ignore
لإخبار Room عن أي منها سيستخدم ولا يجب استخدامه.
@Entity(tableName = "users") public class User { @PrimaryKey @ColumnInfo(name = "userid") private String mId; @ColumnInfo(name = "username") private String mUserName; @ColumnInfo(name = "last_update") private Date mDate; @Ignore public User(String userName) { mId = UUID.randomUUID().toString(); mUserName = userName; mDate = new Date(System.currentTimeMillis()); } public User(String id, String userName, Date date) { this.mId = id; this.mUserName = userName; this.mDate = date; } ... }
ملاحظة: @Entity
، @ColumnInfo
إلى أسماء الجداول والأعمدة في التطبيق الأصلي وتأكد من تعيينها بشكل صحيح في @ColumnInfo
و @ColumnInfo
.
الخطوة 3. إنشاء كائنات الوصول إلى البيانات (DAO)
DAOs هي المسؤولة عن تحديد أساليب الوصول إلى قاعدة البيانات. في التنفيذ الأولي لمشروع SQLite لدينا ، تم تنفيذ جميع استعلامات قاعدة البيانات في فئة LocalUserDataSource
، حيث عملنا مع كائنات Cursor
. في الغرفة ، لا نحتاج إلى كل الشفرة المرتبطة بالمؤشر ، ويمكننا ببساطة تحديد طلباتنا باستخدام التعليقات التوضيحية في فئة UserDao
.
على سبيل المثال ، عند الاستعلام عن جميع المستخدمين من قاعدة البيانات ، يقوم Room بكل "العمل الشاق" ، ونحن بحاجة فقط إلى الكتابة:
@Query(“SELECT * FROM Users”) List<User> getUsers();
الخطوة 4. إنشاء قاعدة بيانات
لقد حددنا بالفعل جدول Users
لدينا واستفساراته المقابلة ، لكننا لم نقم بعد بإنشاء قاعدة بيانات توحد جميع مكونات الغرفة هذه. للقيام بذلك ، نحتاج إلى تعريف فئة مجردة يمتد RoomDatabase
. هذه الفئة تحمل علامة @Database
، والتي تسرد الكائنات الموجودة في قاعدة البيانات و DAO التي تصل إليها. يجب زيادة نسخة قاعدة البيانات بنسبة 1 مقارنة بالقيمة الأصلية ، لذلك في حالتنا ستكون 2.
@Database(entities = {User.class}, version = 2) @TypeConverters(DateConverter.class) public abstract class UsersDatabase extends RoomDatabase { private static UsersDatabase INSTANCE; public abstract UserDao userDao();
نظرًا لأننا نريد حفظ بيانات المستخدم ، نحتاج إلى تنفيذ غرفة إخبار فئة Migration
بما يجب القيام به عند التبديل من الإصدار 1 إلى 2. في حالتنا ، نظرًا لأن مخطط قاعدة البيانات لم يتغير ، فنحن ببساطة نقدم تطبيقًا فارغًا:
static final Migration MIGRATION_1_2 = new Migration(1, 2) { @Override public void migrate(SupportSQLiteDatabase database) {
قم بإنشاء كائن قاعدة بيانات في فئة UsersDatabase
عن طريق تحديد اسم قاعدة البيانات UsersDatabase
:
database = Room.databaseBuilder(context.getApplicationContext(), UsersDatabase.class, "Sample.db") .addMigrations(MIGRATION_1_2) .build();
لمعرفة المزيد حول كيفية تنفيذ ترحيل قاعدة البيانات وكيف تعمل تحت الغطاء ، راجع هذا المنشور .
الخطوة 5. تحديث مستودع لاستخدام الغرفة
لقد أنشأنا قاعدة البيانات الخاصة بنا ، وجدول المستخدم والاستعلامات ، لذلك حان الوقت لاستخدامها. في هذه المرحلة ، سنقوم بتحديث فئة LocalUserDataSource
لاستخدام أساليب UserDao
. للقيام بذلك ، نقوم أولاً بتحديث المنشئ: قم بإزالة Context
وقم بإضافة UserDao
. بالطبع ، يجب أيضًا تحديث أي فئة تقوم بإنشاء مثيل لـ LocalUserDataSource
.
بعد ذلك ، سنقوم بتحديث أساليب LocalUserDataSource
التي تجعل الاستعلامات عن طريق استدعاء أساليب UserDao
. على سبيل المثال ، تبدو الطريقة التي تطلب من جميع المستخدمين الآن كما يلي:
public List<User> getUsers() { return mUserDao.getUsers(); }
والآن حان الوقت لإطلاق ما فعلناه.
واحدة من أفضل ميزات Room هي أنه إذا أجريت عمليات قاعدة بيانات على سلسلة الرسائل الرئيسية ، فسوف يتعطل التطبيق الخاص بك مع رسالة الخطأ التالية:
java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time.
إحدى الطرق الموثوقة لنقل عمليات الإدخال / الإخراج من مؤشر الترابط الرئيسي هي إنشاء Runnable
جديد من شأنه إنشاء مؤشر ترابط جديد لكل استعلام قاعدة البيانات. نظرًا لأننا نستخدم هذا النهج بالفعل في متغير sqlite ، فلم تكن هناك حاجة للتغييرات.
الخطوة 6. اختبار على الجهاز
أنشأنا فئات جديدة - UserDao
و UsersDatabase
بتغيير LocalUserDataSource
لدينا لاستخدام قاعدة بيانات الغرفة. الآن نحن بحاجة لاختبار لهم.
اختبار UserDao
لاختبار UserDao
، نحتاج إلى إنشاء فئة اختبار AndroidJUnit4
. الميزة المذهلة لـ Room هي القدرة على إنشاء قاعدة بيانات في الذاكرة. هذا يلغي الحاجة إلى التنظيف بعد كل اختبار.
@Before public void initDb() throws Exception { mDatabase = Room.inMemoryDatabaseBuilder( InstrumentationRegistry.getContext(), UsersDatabase.class) .build(); }
نحتاج أيضًا إلى التأكد من إغلاق اتصال قاعدة البيانات بعد كل اختبار.
@After public void closeDb() throws Exception { mDatabase.close(); }
على سبيل المثال ، لاختبار تسجيل دخول المستخدم ، نضيف مستخدمًا ثم نتحقق مما إذا كان بإمكاننا الحصول على هذا المستخدم من قاعدة البيانات.
@Test public void insertAndGetUser() {
اختبار استخدام UserDao في LocalUserDataSource
من السهل التأكد من أن LocalUserDataSource
لا يزال يعمل بشكل صحيح ، لأن لدينا بالفعل اختبارات تصف سلوك هذه الفئة. كل ما نحتاج إليه هو إنشاء قاعدة بيانات في الذاكرة ، والحصول على كائن UserDao
منه ، واستخدامه كمعلمة LocalUserDataSource
.
@Before public void initDb() throws Exception { mDatabase = Room.inMemoryDatabaseBuilder( InstrumentationRegistry.getContext(), UsersDatabase.class) .build(); mDataSource = new LocalUserDataSource(mDatabase.userDao()); }
مرة أخرى ، نحتاج إلى التأكد من إغلاق قاعدة البيانات بعد كل اختبار.
اختبار ترحيل قاعدة البيانات
يمكنك قراءة المزيد حول كيفية تطبيق اختبارات ترحيل قاعدة البيانات ، وكذلك كيفية عمل MigrationTestHelper
، في هذا المنشور .
يمكنك أيضًا مشاهدة الرمز من مثال تطبيق الترحيل أكثر تفصيلاً.
الخطوة 7. إزالة جميع لا لزوم لها
قم بإزالة أي فئات وأسطر من التعليمات البرمجية غير المستخدمة يتم استبدالها الآن بوظيفة الغرفة. في مشروعنا ، نحتاج فقط إلى إزالة فئة UsersDbHelper
، التي وسعت فئة SQLiteOpenHelper
.
إذا كان لديك قاعدة بيانات أكبر وأكثر تعقيدًا ، وتريد التبديل تدريجياً إلى Room ، فإننا نوصي بهذا المنشور .
الآن انخفض حجم التعليمات البرمجية القياسية المعرضة للخطأ ، ويتم فحص الطلبات في وقت الترجمة ، ويتم اختبار كل شيء. في 7 خطوات بسيطة ، تمكنا من نقل تطبيقنا الحالي إلى Room. يمكنك رؤية تطبيق مثال هنا .