Android Kotlin Fundamentals 06.1: إنشاء قاعدة بيانات للغرفة

يشكّل هذا الدرس التطبيقي جزءًا من الدورة التدريبية لأساسيات Android Kotlin. ستحصل على أقصى قيمة ممكنة من هذه الدورة التدريبية إذا كنت تستخدم الدروس التطبيقية حول الترميز بشكل متسلسل. يتم إدراج جميع الدروس التطبيقية حول ترميز الدورات التدريبية في الصفحة المقصودة لدروس الترميز Android Kotlin Fundamentals.

مقدمة

تتضمّن معظم التطبيقات بيانات يجب الاحتفاظ بها، حتى بعد إغلاق المستخدم للتطبيق. على سبيل المثال، قد يخزِّن التطبيق قائمة تشغيل أو مستودعًا لعناصر اللعبة أو سجلات للنفقات أو الدخل أو كتالوجًا لمجموعات نجمية أو بيانات عن النوم بمرور الوقت. ويمكنك عادةً استخدام قاعدة بيانات لتخزين البيانات الدائمة.

Room هي مكتبة قاعدة بيانات تمثل جزءًا من Jetpack لنظام التشغيل Android. يتولى Room الكثير من مهام إعداد قاعدة البيانات وضبطها، كما يسمح لتطبيقك بالتفاعل مع قاعدة البيانات باستخدام استدعاءات الوظائف العادية. في الخيارات المتقدمة، Room هو طبقة تجريدية أعلى قاعدة بيانات SQLite. مصطلحات Room's وبنية طلب البحث لطلبات البحث الأكثر تعقيدًا، اتبع نموذج SQLite.

توضّح الصورة أدناه مدى توافق قاعدة بيانات Room مع البنية العامة المقترحة في هذه الدورة التدريبية.

ما يجب معرفته

ويجب أن تكون على دراية بما يلي:

  • إنشاء واجهة مستخدم أساسية لأحد تطبيقات Android
  • استخدام الأنشطة والأجزاء والعروض.
  • الانتقال بين الأجزاء واستخدام Smart Args (مكوّن إضافي في Gradle) لتمرير البيانات بين الأجزاء.
  • عرض النماذج ومصانع نماذج العرض وLiveData ومراصدها يمكنك الاطّلاع على المواضيع التالية حول المكوّنات المعمارية في درس تطبيقي سابق حول الترميز في هذه الدورة التدريبية.
  • فهم أساسي لقواعد بيانات SQL ولغة SQLite. اطّلِع على SQLite Primer للحصول على نظرة عامة سريعة أو تذكير.

ما ستتعرَّف عليه

  • كيفية إنشاء قاعدة بيانات Room والتفاعل معها للاحتفاظ بالبيانات.
  • كيفية إنشاء فئة بيانات تحدّد جدولاً في قاعدة البيانات.
  • كيفية استخدام كائن الوصول إلى البيانات (DAO) لربط وظائف Kotlin بطلبات بحث SQL.
  • كيفية اختبار ما إذا كانت قاعدة البيانات تعمل أم لا.

الإجراءات التي ستنفذّها

  • إنشاء قاعدة بيانات لتطبيق Room بواجهة لبيانات النوم ليلاً.
  • اختبر قاعدة البيانات باستخدام الاختبارات المقدمة.

في هذا الدرس التطبيقي حول الترميز، تُنشئ جزء قاعدة البيانات من تطبيق يتتبَّع جودة النوم. يستخدم التطبيق قاعدة بيانات لتخزين بيانات النوم بمرور الوقت.

يحتوي التطبيق على شاشتين، تمثلهما الأجزاء، كما هو موضّح في الشكل التالي.

تحتوي الشاشة الأولى، الموضحة على اليمين، على أزرار لبدء التتبع وإيقافه. تعرض الشاشة جميع بيانات النوم للمستخدم. يعمل الزر محو على حذف جميع البيانات التي جمعها التطبيق للمستخدم نهائيًا.

الشاشة الثانية، التي تظهر على اليسار، مخصّصة لاختيار تقييم جودة النوم. في التطبيق، يتم تمثيل التقييم رقميًا. لأغراض التطوير، يعرض التطبيق كلاً من رموز الوجه والمعادلات الرقمية لها.

تكون خطوات المستخدم على النحو التالي:

  • يفتح المستخدم التطبيق وتظهر له شاشة تتبُّع النوم.
  • ينقر المستخدم على الزر ابدأ. يسجِّل هذا العمود وقت البدء ويعرضه. يكون زر البدء غير مفعّل، والزر إيقاف مفعّل.
  • ينقر المستخدم على الزر إيقاف. ويتم تسجيل وقت الانتهاء وفتح شاشة جودة النوم.
  • ويختار المستخدم رمز جودة النوم. ويتم إغلاق الشاشة، كما تعرض شاشة التتبُّع وقت انتهاء النوم وجودة النوم. الزر إيقاف غير مفعّل والزر ابدأ مفعّل. التطبيق جاهز لليلة أخرى.
  • يتم تفعيل الزر محو عند توفّر بيانات في قاعدة البيانات. عندما ينقر المستخدم على الزر محو، يتم محو جميع بياناته بدون اللجوء إلى الخطأ - لا تظهر هذه الرسالة&كذلك، هل أنت متأكد؟

يستخدم هذا التطبيق بنية مبسطة، كما هو موضح أدناه في سياق البنية الكاملة. يستخدم التطبيق المكونات التالية فقط:

  • وحدة تحكُّم واجهة المستخدم
  • عرض النموذج وLiveData
  • قاعدة بيانات الغرفة

الخطوة 1: تنزيل تطبيق بدء التشغيل وتشغيله

  1. نزِّل تطبيق TrackMySleepquality-Starter من GitHub.
  2. إنشاء التطبيق وتشغيله. يعرض التطبيق واجهة المستخدم للجزء SleepTrackerFragment، ولكن بدون بيانات. الأزرار لا تستجيب للنقرات.

الخطوة 2: فحص تطبيق إجراء التفعيل

  1. الاطّلاع على ملفات Gradle:
  • ملف Gradle للمشروع
    في ملف build.gradle على مستوى المشروع، لاحظ المتغيرات التي تحدد إصدارات المكتبة. تعمل الإصدارات المُستخدَمة في تطبيق المبتدئين معًا بشكلٍ جيد، كما أنها تعمل بشكل جيد مع هذا التطبيق. وعند الانتهاء من هذا الدرس التطبيقي حول الترميز، قد يطلب منك "استوديو Android" تحديث بعض الإصدارات. يرجع الأمر إليك عما إذا كنت تريد تحديث الإصدارات الموجودة في التطبيق أو البقاء فيها. إذا واجهت أخطاء "strange" مجمّعة، جرِّب استخدام مجموعة إصدارات المكتبة التي يستخدمها تطبيق الحل النهائي.
  • ملف Gradle الخاص بالوحدة. لاحِظ أنّها تعتمد على جميع الارتباطات في مكتبات Android Jetpack، بما في ذلك Room واعتماديات الكوروتين.
  1. ألقِ نظرة على الحزم وواجهة المستخدم. تم تنظيم التطبيق حسب الوظائف. تحتوي الحزمة على ملفات عناصر نائبة حيث يمكنك إضافة الرمز إلى هذه السلسلة من الدروس التطبيقية حول الترميز.
  • database حزمة، لكل الرموز المتعلقة بقاعدة بيانات Room.
  • تحتوي حزمتاsleepquality وsleeptracker على الجزء ونموذج العرض ومصنع نموذج العرض لكل شاشة.
  1. يمكنك إلقاء نظرة على ملف Util.kt الذي يحتوي على وظائف للمساعدة في عرض بيانات جودة النوم. تم التعليق على بعض الرموز لأنها تشير إلى نموذج عرض تنشئه لاحقًا.
  2. اطّلِع على مجلد androidTest (SleepDatabaseTest.kt). وستستخدم هذا الاختبار للتحقّق من أن قاعدة البيانات تعمل على النحو المطلوب.

في نظام التشغيل Android، يتم تمثيل البيانات في فئات البيانات، ويتم الوصول إلى البيانات وتعديلها باستخدام طلبات الوظائف. ومع ذلك، في عالم قاعدة البيانات، تحتاج إلى الكيانات وطلبات البحث.

  • يمثل الكيان كائنًا أو مفهومًا وخصائصه لتخزينها في قاعدة البيانات. تحدد فئة الكيان جدولاً، ويمثّل كل مثيل لذلك الصف صفًا في الجدول. تحدد كل خاصية عمودًا. في تطبيقك، سيحتفظ الكيان بمعلومات حول الليل.
  • طلب البحث هو طلب للحصول على بيانات أو معلومات من جدول قاعدة بيانات أو مجموعة من الجداول، أو طلب تنفيذ إجراء على البيانات. ومن طلبات البحث الشائعة، الحصول على الكيانات وإدراجها وتعديلها. على سبيل المثال، يمكنك طلب البحث عن جميع ليالي النوم المسجّلة، مرتّبة حسب وقت البدء.

وتنجز Room جميع المهام الصعبة للحصول على من فئات بيانات Kotlin إلى الكيانات التي يمكن تخزينها في جداول SQLite، ومن بيانات الدالة إلى طلبات بحث SQL.

يجب تحديد كل كيان كفئة بيانات تتضمن تعليقات توضيحية، والتفاعلات كواجهة تتضمن تعليقات توضيحية، وكائن الوصول إلى البيانات (DAO). يستخدم Room هذه الصفوف التي تم التعليق عليها لإنشاء الجداول في قاعدة البيانات، وطلبات البحث التي تعمل على قاعدة البيانات.

الخطوة 1: إنشاء كيان SleepNight

في هذه المهمة، يمكنك تحديد ليلة واحدة من النوم كفئة بيانات تتضمّن تعليقات توضيحية.

عند تسجيل ليلة واحدة للنوم، عليك تسجيل وقت البدء ووقت الانتهاء وتقييم الجودة.

ويجب أيضًا توفّر بطاقة تعريف لتحديد الليل بشكل فريد.

  1. في حزمة database، ابحث عن ملف SleepNight.kt وافتحه.
  2. يمكنك إنشاء فئة بيانات SleepNight باستخدام معلّمات لرقم تعريف ووقت البدء (بالمللي ثانية) ووقت الانتهاء (بالمللي ثانية) وتقييم رقمي لجودة النوم.
  • يجب إعداد sleepQuality، لذا يجب ضبطه على -1، ما يشير إلى أنّه لم يتم جمع أي بيانات للجودة.
  • وعليك أيضًا إعداد وقت الانتهاء. اضبطه على وقت البدء للإشارة إلى عدم تسجيل أي وقت انتهاء بعد.
data class SleepNight(
       var nightId: Long = 0L,
       val startTimeMilli: Long = System.currentTimeMillis(),
       var endTimeMilli: Long = startTimeMilli,
       var sleepQuality: Int = -1
)
  1. قبل الإعلان عن الصف الدراسي، يمكنك إضافة تعليقات توضيحية إلى فئة البيانات باستخدام @Entity. أدخِل اسمًا للجدول daily_sleep_quality_table. ويكون وسيطة tableName اختيارية، ولكن ننصح بها. يمكنك البحث عن وسيطات أخرى في المستندات.

    في حال المطالبة بذلك، يمكنك استيراد Entity وجميع التعليقات التوضيحية الأخرى من المكتبة androidx.
@Entity(tableName = "daily_sleep_quality_table")
data class SleepNight(...)
  1. لتحديد nightId باعتباره المفتاح الأساسي، يمكنك إضافة تعليقات توضيحية إلى السمة nightId باستخدام @PrimaryKey. اضبط المعلمة autoGenerate على true بحيث تُنشئ Room رقم التعريف لكل كيان. وهذا يضمن أن يكون رقم التعريف لكل ليلة فريدًا.
@PrimaryKey(autoGenerate = true)
var nightId: Long = 0L,...
  1. يمكنك إضافة تعليقات توضيحية إلى الخصائص المتبقية باستخدام @ColumnInfo. يمكنك تخصيص أسماء المواقع باستخدام المعلَمات كما هو موضح أدناه.
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "daily_sleep_quality_table")
data class SleepNight(
       @PrimaryKey(autoGenerate = true)
       var nightId: Long = 0L,

       @ColumnInfo(name = "start_time_milli")
       val startTimeMilli: Long = System.currentTimeMillis(),

       @ColumnInfo(name = "end_time_milli")
       var endTimeMilli: Long = startTimeMilli,

       @ColumnInfo(name = "quality_rating")
       var sleepQuality: Int = -1
)
  1. أنشئ الرمز وشغِّله للتأكُّد من خلوِّه من الأخطاء.

في هذه المهمة، يمكنك تحديد كائن الوصول إلى البيانات (DAO). على نظام التشغيل Android، توفِّر خدمة DAO طرقًا ملائمة لإدراج قاعدة البيانات وحذفها وتعديلها.

عند استخدام قاعدة بيانات Room، يمكنك تشغيل طلب بحث في قاعدة البيانات من خلال تحديد استدعاءات Kotlin واستدعاؤها. ويتم ربط وظائف Kotlin هذه بطلبات بحث SQL. ويمكنك تحديد عمليات الربط هذه في "إحصاءات Google" باستخدام التعليقات التوضيحية، وسينشئ Room الرمز اللازم.

يمكنك التفكير في استخدام مُعرّف البيانات الرقمي (DAO) على أنه واجهة مخصّصة للوصول إلى قاعدة البيانات الخاصة بك.

لإجراء عمليات قاعدة البيانات الشائعة، توفر مكتبة Room تعليقات توضيحية مريحة، مثل @Insert و@Delete و@Update. بالنسبة إلى كل شيء آخر، هناك تعليق توضيحي من @Query. يمكنك كتابة أي طلب بحث متوافق مع SQLite.

وكمكافأة إضافية، عندما تُنشئ طلبات البحث في "استوديو Android"، تتحقّق برامج التجميع من طلبات بحث SQL بحثًا عن أخطاء في البنية.

يجب أن يكون بإمكانك تنفيذ الإجراءات التالية في قاعدة بيانات تتبُّع النوم لأمسيات النوم:

  • إدراج ليالٍ جديدة.
  • تعديل ليلة حالية لتعديل وقت الانتهاء وتقييم الجودة.
  • الحصول على ليلة معيّنة استنادًا إلى مفتاحها.
  • الحصول على جميع الليالي حتى تتمكّن من عرضها
  • الحصول على أحدث ليلة.
  • احذف جميع الإدخالات في قاعدة البيانات.

الخطوة 1: إنشاء SleepDatabase DAO

  1. في الحزمة database، افتح SleepDatabaseDao.kt.
  2. لاحظ أن interface SleepDatabaseDao تمت إضافة تعليقات توضيحية عليه باستخدام @Dao. يجب إضافة تعليقات توضيحية إلى جميع أجهزة DAO بالكلمة الرئيسية @Dao.
@Dao
interface SleepDatabaseDao {}
  1. داخل نص الواجهة، أضِف تعليقًا توضيحيًا من نوع @Insert. أسفل @Insert، تتم إضافة الدالة insert() التي تستخدم مثيل Entity الفئة SleepNight كوسيطة لها.

    هذا كل ما في الأمر. سينشئ Room جميع الرموز اللازمة لإدراج SleepNight في قاعدة البيانات. عند استدعاء insert() من رمز Kotlin، ينفذ Room طلب SQL لإدراج الكيان في قاعدة البيانات. (ملاحظة: يمكنك استدعاء الدالة لأي شيء تريده).
@Insert
fun insert(night: SleepNight)
  1. أضِف تعليقًا توضيحيًا @Update مع دالة update() لإحدى SleepNight. الكيان الذي تم تحديثه هو الكيان الذي لديه المفتاح نفسه للكيان الذي تم تمريره. يمكنك تحديث بعض خصائص الكيان الأخرى أو جميعها.
@Update
fun update(night: SleepNight)

ليس هناك تعليق توضيحي ملائم للوظائف المتبقية، لذا عليك استخدام التعليق التوضيحي @Query وتقديم طلبات بحث SQLite.

  1. أضِف تعليقًا توضيحيًا @Query مع دالة get() يتضمّن وسيطة Long key ويعرض قيمة SleepNight فارغة. سترى خطأً في المعلمة المفقودة.
@Query
fun get(key: Long): SleepNight?
  1. يتم توفير طلب البحث كمعلَمة سلسلة إلى التعليق التوضيحي. يمكنك إضافة معلمة إلى @Query. حوِّل String إلى طلب بحث SQLite.
  • اختيار جميع الأعمدة من daily_sleep_quality_table
  • WHERE تتطابق nightId مع الوسيطة :key.

    يُرجى ملاحظة :key. يمكنك استخدام تدوين النقطتين الرأسيتين في طلب البحث للإشارة إلى الوسيطات في الدالة.
("SELECT * from daily_sleep_quality_table WHERE nightId = :key")
  1. يمكنك إضافة @Query آخر باستخدام دالة clear() وطلب بحث SQLite إلى DELETE كل شيء من daily_sleep_quality_table. لا يحذف طلب البحث هذا الجدول نفسه.

    يحذف التعليق التوضيحي @Delete عنصرًا واحدًا، ويمكنك استخدام @Delete وتقديم قائمة بالليالي لحذفها. عيب ذلك أنك تحتاج إلى استرجاع أو معرفة ما هو متوفّر في الجدول. يُعد التعليق التوضيحي @Delete رائعًا لحذف إدخالات معيّنة، ولكنه غير فعّال لمحو جميع الإدخالات من الجدول.
@Query("DELETE FROM daily_sleep_quality_table")
fun clear()
  1. يمكنك إضافة @Query مع الدالة getTonight(). يمكنك عرض SleepNight فارغًا من خلال getTonight()، بحيث يمكن للدالة معالجة الحالة التي يكون فيها الجدول فارغًا. (الجدول فارغ في البداية وبعد محو البيانات).

    للحصول على "tonight" من قاعدة البيانات، اكتب طلب بحث SQL يعرض العنصر الأول من قائمة النتائج التي يتم طلبها حسب nightId تنازليًا. استخدِم LIMIT 1 لعرض عنصر واحد فقط.
@Query("SELECT * FROM daily_sleep_quality_table ORDER BY nightId DESC LIMIT 1")
fun getTonight(): SleepNight?
  1. أضِف @Query مع الدالة getAllNights():
  • اطلب من طلب SQLite عرض جميع الأعمدة من daily_sleep_quality_table، مرتبةً تنازليًا.
  • اطلب من getAllNights() عرض قائمة بالكيانات SleepNight بصفتها LiveData. يحافظ Room على تحديث LiveData نيابةً عنك، ما يعني أنك لن تحتاج إلى الحصول على البيانات إلا مرة واحدة فقط.
  • قد تحتاج إلى استيراد LiveData من androidx.lifecycle.LiveData.
@Query("SELECT * FROM daily_sleep_quality_table ORDER BY nightId DESC")
fun getAllNights(): LiveData<List<SleepNight>>
  1. على الرغم من أنك لن ترى أي تغييرات مرئية، يمكنك تشغيل تطبيقك للتأكد من عدم حدوث أخطاء.

في هذه المهمة، يمكنك إنشاء قاعدة بيانات Room تستخدم Entity وDAO اللذين أنشأتهما في المهمة السابقة.

يجب إنشاء صف مجرّد لصاحب قاعدة البيانات، مع إضافة تعليقات توضيحية إلى @Database. تتضمن هذه الفئة طريقة واحدة تنشئ مثيلاً لقاعدة البيانات إذا لم تكن قاعدة البيانات متوفرة أو تعرض مرجعًا لقاعدة بيانات حالية.

يتطلّب الحصول على قاعدة بيانات Room بعض الشيء، لذا إليك هذه العملية العامة قبل البدء باستخدام الرمز:

  • أنشئ صفًا واحدًا (public abstract) يكون extends RoomDatabase. تم تخصيص هذا الصف كصاحب قاعدة بيانات. الصف مجرد، لأن Room ينشئ التنفيذ من أجلك.
  • يمكنك إضافة تعليقات توضيحية إلى الصف @Database. في الوسيطات، أعلن عن الكيانات لقاعدة البيانات وحدّد رقم الإصدار.
  • داخل العنصر companion، حدّد طريقة مجردة أو خاصية تعرض السمة SleepDatabaseDao. سينشئ Room النص نيابةً عنك.
  • أنت تحتاج فقط إلى مثيل واحد من قاعدة بيانات Room للتطبيق بأكمله، لذا اجعل RoomDatabase أحاديًا.
  • استخدم أداة إنشاء قاعدة بيانات Room's لإنشاء قاعدة البيانات فقط إذا لم تتوفر قاعدة البيانات. بخلاف ذلك، يمكنك عرض قاعدة البيانات الحالية.

الخطوة 1: إنشاء قاعدة البيانات

  1. في الحزمة database، افتح SleepDatabase.kt.
  2. في الملف، أنشِئ صف abstract باسم SleepDatabase يمتد إلى RoomDatabase.

    يمكنك إضافة تعليق توضيحي إلى الصف باستخدام @Database.
@Database()
abstract class SleepDatabase : RoomDatabase() {}
  1. سترى خطأً بشأن الكيانات ومَعلمات الإصدار غير المتوفّرة. يتطلب التعليق التوضيحي @Database عدة وسيطات، حتى تتمكن Room من إنشاء قاعدة البيانات.
  • قدِّم السمة SleepNight باعتبارها العنصر الوحيد الذي يتضمّن قائمة entities.
  • ضبط version كطريقة دفع 1 كلما غيّرت المخطط، يجب زيادة رقم الإصدار.
  • اضبط exportSchema على false، بحيث لا يتم الاحتفاظ بالنُسخ الاحتياطية لسجلّ إصدارات المخطط.
entities = [SleepNight::class], version = 1, exportSchema = false
  1. تحتاج قاعدة البيانات إلى الحصول على معلومات عن DAO. داخل نص الصف الدراسي، أعلن عن القيمة المجرّدة التي تعرض SleepDatabaseDao. يمكن أن يكون لديك عدة علامات DAO.
abstract val sleepDatabaseDao: SleepDatabaseDao
  1. وأسفل ذلك، حدِّد كائن companion. يتيح الكائن المصاحب للعملاء الوصول إلى طرق إنشاء قاعدة البيانات أو الحصول عليها بدون إنشاء مثيل للصف. نظرًا لأن الغرض الوحيد من هذه الفئة هو توفير قاعدة بيانات، ليس هناك ما يدعو إلى إنشاء مثيل لها على الإطلاق.
 companion object {}
  1. داخل الكائن companion، يمكنك تعريف متغيّر خاص قابل للإفراغ INSTANCE لقاعدة البيانات وإعداده على null. سيحتفظ المتغير INSTANCE بمرجع في قاعدة البيانات، بعد إنشاء واحد. يساعدك ذلك على تجنُّب فتح الاتصالات بقاعدة البيانات بشكل متكرر، وهو أمر مكلف.

يمكنك إضافة تعليقات توضيحية إلى INSTANCE باستخدام @Volatile. لن يتم مطلقًا تخزين قيمة المتغير المتذبذب مؤقتًا، وسيتم إجراء جميع عمليات القراءة والقراءة من الذاكرة الرئيسية وإليها. يساعد ذلك على التأكد من أن قيمة INSTANCE محدّثة دائمًا بالقيمة نفسها لجميع سلاسل المحادثات التي يتم تنفيذها. ويعني ذلك أنّ التغييرات التي تجريها سلسلة محادثات واحدة على INSTANCE ستكون مرئية لجميع سلاسل المحادثات الأخرى على الفور، ولن تظهر لك رسالة مفادها أنّ كلاً من سلسلة المحادثات يؤدي إلى تعديل الكيان نفسه في ذاكرة التخزين المؤقت، ما قد يؤدي إلى حدوث مشكلة.

@Volatile
private var INSTANCE: SleepDatabase? = null
  1. أسفل INSTANCE، مع الاستمرار في داخل الكائن companion، حدّد getInstance()طريقة باستخدام معلمة Context التي ستحتاج إليها أداة إنشاء قواعد البيانات. عرض نوع SleepDatabase ستظهر لك رسالة خطأ لأن getInstance() لا يعرض أي شيء حتى الآن.
fun getInstance(context: Context): SleepDatabase {}
  1. داخل getInstance()، أضِف كتلة synchronized{}. إدخال this حتى تتمكّن من الوصول إلى السياق.

    من المحتمل أن تطلب سلاسل محادثات متعددة إنشاء مثيل قاعدة بيانات في الوقت نفسه، ما يؤدي إلى إنشاء قاعدتي بيانات بدلاً من قاعدة واحدة. من غير المحتمل أن تحدث هذه المشكلة في نموذج التطبيق هذا، ولكنها ممكنة في تطبيق أكثر تعقيدًا. عند إلحاق الرمز لإدخال قاعدة البيانات في synchronized، لا يمكن إدخال سوى سلسلة محادثات واحدة فقط للتنفيذ في كل مرة، ما يضمن إعداد قاعدة البيانات مرة واحدة فقط.
synchronized(this) {}
  1. داخل الكتلة المتزامنة، انسخ القيمة الحالية INSTANCE إلى المتغير المحلي instance. وهذا للاستفادة من ميزة البث الذكي التي لا تتوفر إلا للمتغيرات المحلية.
var instance = INSTANCE
  1. داخل كتلة synchronized، return instance في نهاية المجموعة synchronized. تجاهل الخطأ "غير متطابق في نوع الإرجاع"، ولن تظهر لك أي قيم فارغة بعد الانتهاء.
return instance
  1. في أعلى عبارة return، أضِف عبارة if للتحقق مما إذا كانت السياسة instance فارغة، أي ليست هناك قاعدة بيانات حتى الآن.
if (instance == null) {}
  1. إذا كانت instance هي null، استخدِم أداة إنشاء قواعد البيانات للحصول على قاعدة بيانات. في نص عبارة if، يمكنك استدعاء Room.databaseBuilder وتقديم السياق الذي أدخلته وفئة قاعدة البيانات واسم لقاعدة البيانات sleep_history_database. لإزالة الخطأ، سيكون عليك إضافة استراتيجية نقل البيانات وbuild() في الخطوات التالية.
instance = Room.databaseBuilder(
                           context.applicationContext,
                           SleepDatabase::class.java,
                           "sleep_history_database")
  1. أضِف استراتيجية نقل البيانات المطلوبة إلى "أداة الإنشاء". استخدام .fallbackToDestructiveMigration().

    عادةً ما تحتاج إلى توفير عنصر نقل بيانات مع استراتيجية نقل البيانات عند تغيُّر المخطط. كائن الترحيل هو كائن يحدد كيفية نقل جميع الصفوف باستخدام المخطط القديم وتحويلها إلى صفوف في المخطط الجديد، بحيث لا يتم فقدان أي بيانات. تتجاوز عملية نقل البيانات نطاق هذا الدرس التطبيقي حول الترميز. يتمثل أحد الحلول البسيطة في إتلاف قاعدة البيانات وإعادة إنشائها، مما يعني فقدان البيانات.
.fallbackToDestructiveMigration()
  1. أَخِيرًا، اتَّصِلْ بِـ .build().
.build()
  1. تحديد INSTANCE = instance كخطوة نهائية داخل كشف if.
INSTANCE = instance
  1. ويجب أن يظهر الرمز النهائي بالشكل التالي:
@Database(entities = [SleepNight::class], version = 1, exportSchema = false)
abstract class SleepDatabase : RoomDatabase() {

   abstract val sleepDatabaseDao: SleepDatabaseDao

   companion object {

       @Volatile
       private var INSTANCE: SleepDatabase? = null

       fun getInstance(context: Context): SleepDatabase {
           synchronized(this) {
               var instance = INSTANCE

               if (instance == null) {
                   instance = Room.databaseBuilder(
                           context.applicationContext,
                           SleepDatabase::class.java,
                           "sleep_history_database"
                   )
                           .fallbackToDestructiveMigration()
                           .build()
                   INSTANCE = instance
               }
               return instance
           }
       }
   }
}
  1. إنشاء الرمز وتشغيله

لديك الآن كل العناصر الأساسية للعمل مع قاعدة بيانات Room. يتم تجميع هذا الرمز وتشغيله، ولكن ليست لديك طريقة لمعرفة ما إذا كان يعمل فعليًا أم لا. لذا، الوقت مناسب لإضافة بعض الاختبارات الأساسية.

الخطوة 2: اختبار SleepDatabase

في هذه الخطوة، يمكنك تشغيل الاختبارات المقدمة للتحقق من عمل قاعدة البيانات. ويساعد ذلك على ضمان عمل قاعدة البيانات قبل إنشائها. الاختبارات المقدمة أساسية. بالنسبة إلى تطبيق إنتاج، يمكنك ممارسة جميع الوظائف وطلبات البحث في جميع مشاهِدي البيانات DAO.

يحتوي تطبيق إجراء التفعيل على مجلد androidTest. يحتوي مجلد androidTest هذا على اختبارات الوحدات التي تتضمن أدوات Android، وهي طريقة رائعة للتصريح بأن الاختبارات تحتاج إلى إطار عمل Android، لذا عليك إجراء الاختبارات على جهاز فعلي أو افتراضي. ويمكنك أيضًا إنشاء اختبارات الوحدة الخالصة التي لا تتضمّن إطار عمل Android وتنفيذها.

  1. في "استوديو Android"، في مجلد androidTest، افتح ملف SleepDatabaseTest.
  2. لإلغاء تعليق الرمز، اختَر جميع الرموز التي تم التعليق عليها واضغط على اختصار لوحة المفاتيح Cmd+/ أو Control+/.
  3. ألقِ نظرة على الملف.

في ما يلي شرح سريع لرمز الاختبار لأنّه يشكّل جزءًا آخر من الرمز يمكنك إعادة استخدامه:

  • SleepDabaseTest هو صف اختباري.
  • يحدّد التعليق التوضيحي @RunWith أنّه مخصّص للاختبار، وهو البرنامج الذي يُعِدّ الاختبارات وينفّذها.
  • أثناء الإعداد، يتم تنفيذ الدالة التي تم إدخال تعليقات توضيحية عليها باستخدام @Before، وتُنشئ SleepDatabase في الذاكرة باستخدام SleepDatabaseDao. "In-memory" تعني أن قاعدة البيانات هذه غير محفوظة في نظام الملفات وسيتم حذفها بعد إجراء الاختبارات.
  • وعند إنشاء قاعدة البيانات المتوفّرة في الذاكرة، يطلب الرمز طريقة أخرى خاصة بالاختبار، وهي allowMainThreadQueries. ستظهر لك رسالة خطأ تلقائيًا إذا حاولت تنفيذ طلبات بحث في سلسلة المحادثات الرئيسية. تسمح لك هذه الطريقة بإجراء اختبارات على سلسلة المحادثات الرئيسية، ويجب تنفيذها فقط أثناء الاختبار.
  • في طريقة اختبار تم إدخال تعليقات توضيحية عليها باستخدام @Test، يمكنك إنشاء SleepNight وإدراجه واسترداده، وتأكيد أنها متماثلة. إذا حدث أي خطأ، استبعد استثناءً. في الاختبار الفعلي، ستتوفر لك طرق @Test متعددة.
  • عند الانتهاء من الاختبار، يتم تنفيذ الدالة المزوّدة بتعليقات توضيحية مع @After لإغلاق قاعدة البيانات.
  1. انقر بزر الماوس الأيمن على ملف الاختبار في جزء المشروع واختَر Run 'SleepDatabaseTest'.
  2. بعد إجراء الاختبارات، تحقق في جزء SleepDatabaseTest من اجتياز جميع الاختبارات.

بما أنّ كل الاختبارات قد تم اجتيازها، أصبحت على دراية بعدة أمور:

  • يتم إنشاء قاعدة البيانات بشكل صحيح.
  • يمكنك إدراج SleepNight في قاعدة البيانات.
  • يمكنك استعادة SleepNight.
  • يحتوي SleepNight على القيمة الصحيحة للجودة.

مشروع "استوديو Android": TrackMySleepqualityRoomAndTesting

عند اختبار قاعدة بيانات، يجب أن تمارس جميع الطرق المحددة في جدول البيانات الرقمي (DAO). ولإكمال الاختبار، أضِف الاختبارات ونفِّذها لممارسة طرق DAO الأخرى.

  • حدِّد جداولك كفئات بيانات تحتوي على تعليقات توضيحية باستخدام @Entity. ويمكنك تحديد الخصائص التي تم إدخال تعليقات توضيحية عليها باستخدام @ColumnInfo كأعمدة في الجداول.
  • حدِّد كائن الوصول إلى البيانات (DAO) كواجهة بها تعليقات توضيحية تتضمن @Dao. تربط DAO وظائف Kotlin بطلبات بحث قاعدة البيانات.
  • استخدِم التعليقات التوضيحية لتحديد الدوال @Insert و@Delete و@Update.
  • استخدِم التعليق التوضيحي @Query مع سلسلة طلب بحث SQLite كمعلمة لأي طلبات بحث أخرى.
  • أنشئ فئة ملخصة تحتوي على الدالة getInstance() التي تعرض قاعدة بيانات.
  • استخدِم الاختبارات المقدَّمة من أجل اختبار عمل قاعدة البيانات وDAO على النحو المتوقَّع. ويمكنك استخدام الاختبارات المقدَّمة كنموذج.

دورة Udacity:

مستندات مطوّري برامج Android:

وثائق ومقالات أخرى:

يسرد هذا القسم المهام الدراسية المحتملة للطلاب الذين يعملون من خلال هذا الدرس التطبيقي حول الترميز في إطار دورة تدريبية يُديرها معلِّم. يجب أن ينفِّذ المعلّم ما يلي:

  • يمكنك تخصيص واجب منزلي إذا لزم الأمر.
  • التواصل مع الطلاب بشأن كيفية إرسال الواجبات المنزلية
  • وضع درجات للواجبات المنزلية.

ويمكن للمعلّمين استخدام هذه الاقتراحات بقدر ما يريدون أو بقدر ما يريدون، ويجب عدم التردد في تخصيص أي واجبات منزلية أخرى مناسبة.

إذا كنت تستخدم هذا الدرس التطبيقي بنفسك، يمكنك استخدام هذه الواجبات المنزلية لاختبار معلوماتك.

الإجابة عن هذه الأسئلة

السؤال 1

كيف تشير إلى أنّ صفًا يمثّل كيانًا لتخزينه في قاعدة بيانات Room؟

  • توسيع الصف DatabaseEntity.
  • يمكنك إضافة تعليقات توضيحية إلى الصف @Entity.
  • يمكنك إضافة تعليقات توضيحية إلى الصف @Database.
  • توسيع الصف RoomEntity وإضافة تعليق توضيحي للصف باستخدام @Room.

السؤال 2

DAO (كائن الوصول إلى البيانات) هو واجهة يستخدمها Room لربط وظائف Kotlin لطلبات بحث قاعدة البيانات.

كيف يتم توضيح أن الواجهة تمثل DAO لقاعدة بيانات Room؟

  • مدِّد الواجهة RoomDAO.
  • مدِّد الواجهة EntityDao، ثم نفِّذ الطريقة DaoConnection().
  • إضافة تعليقات توضيحية إلى الواجهة باستخدام @Dao.
  • إضافة تعليقات توضيحية إلى الواجهة باستخدام @RoomConnection.

السؤال 3

أي من العبارات التالية صحيحة في قاعدة البيانات Room؟ يُرجى اختيار جميع الإجابات المناسبة.

  • يمكنك تحديد جداول لقاعدة بيانات Room كفئات بيانات تتضمن تعليقات توضيحية.
  • في حال عرض LiveData من طلب البحث، سيحتفظ Room بآخر LiveData نيابةً عنك إذا تغيّر LiveData.
  • يجب أن تتضمن كل قاعدة بيانات Room قاعدة DAO واحدة فقط.
  • لتحديد فئة كقاعدة بيانات Room، اجعلها فئة فرعية من RoomDatabase وأضف تعليقات توضيحية إليها باستخدام @Database.

السؤال 4

أي من التعليقات التوضيحية التالية يمكنك استخدامه في واجهة @Dao؟ يُرجى اختيار جميع الإجابات المناسبة.

  • @Get
  • @Update
  • @Insert
  • @Query

السؤال 5

كيف يمكنك التحقق من أن قاعدة البيانات تعمل؟ يُرجى اختيار كل الإجابات المناسبة.

  • كتابة الاختبارات الآلية
  • تابع كتابة التطبيق وتشغيله إلى أن يعرض البيانات.
  • استبدل المكالمات بالطرق في واجهة DAO من خلال استدعاءات للطرق المعادلة في الفئة Entity.
  • شغِّل الدالة verifyDatabase() التي توفِّرها مكتبة Room.

ابدأ بالدرس التالي: 6.2 الكوروتينات والغرفة

وللحصول على روابط إلى دروس تطبيقية أخرى حول الترميز في هذه الدورة التدريبية، يُرجى الاطّلاع على الصفحة المقصودة لتطبيق الدروس التطبيقية حول الترميز Kotlin Fundamentals.