هذا الدرس التطبيقي حول الترميز هو جزء من دورة "أساسيات Android Kotlin". يمكنك تحقيق أقصى استفادة من هذه الدورة التدريبية إذا اتبعت ترتيب الخطوات في دروس البرمجة. يتم إدراج جميع الدروس التطبيقية حول الترميز الخاصة بالدورة التدريبية في الصفحة المقصودة للدروس التطبيقية حول الترميز في دورة Android Kotlin Fundamentals.
مقدمة
تحتوي معظم التطبيقات على بيانات يجب الاحتفاظ بها، حتى بعد أن يغلق المستخدم التطبيق. على سبيل المثال، قد يخزّن التطبيق قائمة تشغيل أو مستودعًا لعناصر اللعبة أو سجلات النفقات والدخل أو فهرسًا للأبراج أو بيانات النوم بمرور الوقت. عادةً، يمكنك استخدام قاعدة بيانات لتخزين البيانات الثابتة.
Room
هي مكتبة قواعد بيانات تشكّل جزءًا من Jetpack. تتولّى Room
العديد من المهام الروتينية المتعلقة بإعداد قاعدة البيانات وضبطها، وتتيح لتطبيقك التفاعل مع قاعدة البيانات باستخدام طلبات عادية للدوال. في الخلفية، Room
هي طبقة تجريدية فوق قاعدة بيانات SQLite. تتّبع مصطلحات Room
وبنية الاستعلامات الأكثر تعقيدًا نموذج SQLite.
توضّح الصورة أدناه كيف يتناسب قاعدة بيانات Room
مع البنية العامة المقترَحة في هذه الدورة التدريبية.
ما يجب معرفته
يجب أن تكون على دراية بما يلي:
- إنشاء واجهة مستخدم أساسية لتطبيق Android
- استخدام الأنشطة واللقطات وطرق العرض
- التنقّل بين الأجزاء واستخدام Safe Args (وهي إضافة Gradle) لنقل البيانات بين الأجزاء
- عرض النماذج ونماذج العرض ومصانع نماذج العرض و
LiveData
ومراقبيه تم تناول مواضيع "مكوّنات البنية" هذه في درس تطبيقي سابق في هذه الدورة التدريبية. - فهم أساسي لقواعد بيانات SQL ولغة SQLite يمكنك الاطّلاع على مقدمة عن SQLite للحصول على نظرة عامة سريعة أو تنشيط ذاكرتك.
أهداف الدورة التعليمية
- كيفية إنشاء قاعدة بيانات
Room
والتفاعل معها للاحتفاظ بالبيانات - كيفية إنشاء فئة بيانات تحدّد جدولاً في قاعدة البيانات
- كيفية استخدام عنصر الوصول إلى البيانات (DAO) لربط دوال Kotlin باستعلامات SQL
- كيفية اختبار ما إذا كانت قاعدة البيانات تعمل بشكل سليم
الإجراءات التي ستنفذّها
- أنشئ قاعدة بيانات
Room
تتضمّن واجهة لبيانات النوم الليلية. - اختبِر قاعدة البيانات باستخدام الاختبارات المتوفّرة.
في هذا الدرس التطبيقي حول الترميز، ستنشئ جزء قاعدة البيانات من تطبيق يتتبّع جودة النوم. يستخدم التطبيق قاعدة بيانات لتخزين بيانات النوم بمرور الوقت.
يحتوي التطبيق على شاشتَين، يتم تمثيلهما بلقطات، كما هو موضّح في الشكل أدناه.
تحتوي الشاشة الأولى، المعروضة على اليمين، على أزرار لبدء التتبُّع وإيقافه. تعرِض الشاشة جميع بيانات نوم المستخدم. يؤدي النقر على الزر محو إلى حذف جميع البيانات التي جمعها التطبيق للمستخدم نهائيًا.
الشاشة الثانية، المعروضة على اليسار، مخصّصة لاختيار تقييم لجودة النوم. في التطبيق، يتم تمثيل التقييم رقميًا. لأغراض التطوير، يعرض التطبيق رموز الوجوه وما يعادلها من أرقام.
يكون تدفّق المستخدم على النحو التالي:
- يفتح المستخدم التطبيق وتظهر له شاشة تتبُّع النوم.
- ينقر المستخدم على الزر بدء. تسجّل هذه السمة وقت البدء وتعرضه. يكون زر بدء غير مفعَّل، بينما يكون زر إيقاف مفعَّلاً.
- ينقر المستخدم على الزر إيقاف. يتم تسجيل وقت الانتهاء وفتح شاشة جودة النوم.
- يختار المستخدم رمزًا لجودة النوم. تنغلق الشاشة، وتعرض شاشة التتبُّع وقت انتهاء النوم وجودته. يكون زر إيقاف غير مفعَّل ويكون زر بدء مفعَّلاً. التطبيق جاهز للاستخدام في ليلة أخرى.
- يتم تفعيل الزر محو عندما تكون هناك بيانات في قاعدة البيانات. عندما ينقر المستخدم على الزر محو، يتم محو جميع بياناته بدون رجعة، ولن تظهر الرسالة "هل أنت متأكد؟".
يستخدم هذا التطبيق بنية مبسطة، كما هو موضّح أدناه في سياق البنية الكاملة. يستخدم التطبيق المكوّنات التالية فقط:
- وحدة التحكّم في واجهة المستخدم
- عرض النموذج و
LiveData
- قاعدة بيانات Room
الخطوة 1: تنزيل تطبيق البداية وتشغيله
- نزِّل تطبيق TrackMySleepQuality-Starter من GitHub.
- أنشئ التطبيق وشغِّله. يعرض التطبيق واجهة المستخدم للجزء
SleepTrackerFragment
، ولكن بدون بيانات. لا تستجيب الأزرار للنقرات.
الخطوة 2: فحص تطبيق البداية
- ألقِ نظرة على ملفات Gradle:
- ملف Gradle الخاص بالمشروع
في ملفbuild.gradle
على مستوى المشروع، لاحظ المتغيرات التي تحدّد إصدارات المكتبة. تتكامل الإصدارات المستخدَمة في تطبيق المبتدئين بشكل جيد، وتعمل بشكل جيد مع هذا التطبيق. وعند الانتهاء من هذا الدرس البرمجي، قد يطلب منك "استوديو Android" تحديث بعض الإصدارات. يعود إليك قرار التحديث أو البقاء على الإصدارات المتوفّرة في التطبيق. إذا واجهت أخطاء "غريبة" في التجميع، جرِّب استخدام مجموعة إصدارات المكتبة التي يستخدمها تطبيق الحل النهائي. - ملف Gradle الخاص بالوحدة: لاحظ التبعيات المتوفّرة لجميع مكتبات Android Jetpack، بما في ذلك
Room
، والتبعيات الخاصة بالروتينات الفرعية.
- اطّلِع على الحِزم وواجهة المستخدم. يتم تنظيم التطبيق حسب الوظائف. تحتوي الحزمة على ملفات عناصر نائبة ستضيف إليها الرمز البرمجي خلال هذه السلسلة من دروس البرمجة.
- حزمة
database
، لجميع الرموز البرمجية المتعلقة بقاعدة بياناتRoom
- تحتوي حزم
sleepquality
وsleeptracker
على الجزء ونموذج العرض ومصنع نموذج العرض لكل شاشة.
- اطّلِع على ملف
Util.kt
الذي يحتوي على دوال للمساعدة في عرض بيانات جودة النوم. تمت إضافة تعليقات إلى بعض الرموز لأنّها تشير إلى نموذج عرض ستنشئه لاحقًا. - ألقِ نظرة على مجلد androidTest (
SleepDatabaseTest.kt
). ستستخدم هذا الاختبار للتأكّد من أنّ قاعدة البيانات تعمل على النحو المطلوب.
في Android، يتم تمثيل البيانات في فئات البيانات، ويتم الوصول إلى البيانات وتعديلها باستخدام استدعاءات الدوال. ومع ذلك، في عالم قواعد البيانات، تحتاج إلى كيانات وطلبات بحث.
- يمثّل الكيان عنصرًا أو مفهومًا وخصائصه التي سيتم تخزينها في قاعدة البيانات. تحدّد فئة الكيان جدولاً، ويمثّل كل مثيل من هذه الفئة صفًا في الجدول. تحدّد كل سمة عمودًا. في تطبيقك، سيحتوي العنصر على معلومات حول ليلة نوم.
- الاستعلام هو طلب للحصول على بيانات أو معلومات من جدول قاعدة بيانات أو مجموعة من الجداول، أو طلب لتنفيذ إجراء على البيانات. الاستعلامات الشائعة هي تلك التي يتم إجراؤها للحصول على الكيانات وإدراجها وتعديلها. على سبيل المثال، يمكنك طلب البحث عن جميع ليالي النوم المسجّلة، مع ترتيبها حسب وقت البدء.
تتولّى Room
جميع المهام الصعبة نيابةً عنك للانتقال من فئات بيانات Kotlin إلى عناصر يمكن تخزينها في جداول SQLite، ومن تعريفات الدوال إلى طلبات بحث SQL.
يجب تحديد كل عنصر كفئة بيانات مزوّدة بتعليقات توضيحية، والتفاعلات كواجهة مزوّدة بتعليقات توضيحية، أي كائن الوصول إلى البيانات (DAO). تستخدم Room
هذه الفئات المشروحة لإنشاء جداول في قاعدة البيانات، والاستعلامات التي تعمل على قاعدة البيانات.
الخطوة 1: إنشاء كيان SleepNight
في هذه المهمة، ستعرّف ليلة نوم واحدة على أنّها فئة بيانات مشروحة.
لتسجيل ليلة نوم واحدة، عليك تسجيل وقت البدء ووقت الانتهاء وتقييم الجودة.
ويجب أن يكون لديك معرّف لتحديد الليلة بشكل فريد.
- في حزمة
database
، ابحث عن الملفSleepNight.kt
وافتحه. - أنشئ فئة البيانات
SleepNight
مع مَعلمات لمعرّف ووقت بدء (بالملّي ثانية) ووقت انتهاء (بالملّي ثانية) وتقييم رقمي لجودة النوم.
- عليك تهيئة
sleepQuality
، لذا اضبطه على-1
، ما يشير إلى أنّه لم يتم جمع أي بيانات جيدة. - عليك أيضًا ضبط وقت الانتهاء. اضبطها على وقت البدء للإشارة إلى أنّه لم يتم تسجيل وقت انتهاء بعد.
data class SleepNight(
var nightId: Long = 0L,
val startTimeMilli: Long = System.currentTimeMillis(),
var endTimeMilli: Long = startTimeMilli,
var sleepQuality: Int = -1
)
- قبل تعريف الفئة، أضِف التعليق التوضيحي
@Entity
إلى فئة البيانات. أدخِل اسمًا للجدولdaily_sleep_quality_table
. الوسيطة الخاصة بـtableName
اختيارية، ولكن يُنصح باستخدامها. يمكنك البحث عن وسيطات أخرى في المستندات.
إذا طُلب منك ذلك، استورِدEntity
وجميع التعليقات التوضيحية الأخرى من مكتبةandroidx
.
@Entity(tableName = "daily_sleep_quality_table")
data class SleepNight(...)
- لتحديد
nightId
كمفتاح أساسي، أضِف التعليق التوضيحي@PrimaryKey
إلى السمةnightId
. اضبط المَعلمةautoGenerate
علىtrue
لكي ينشئRoom
المعرّف لكل كيان. ويضمن ذلك أن يكون المعرّف لكل ليلة فريدًا.
@PrimaryKey(autoGenerate = true)
var nightId: Long = 0L,...
- أضِف التعليقات التوضيحية إلى السمات المتبقية باستخدام
@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
)
- أنشئ التعليمات البرمجية وشغِّلها للتأكّد من عدم وجود أخطاء.
في هذه المهمة، عليك تحديد عنصر الوصول إلى البيانات (DAO). على Android، توفّر DAO طرقًا سهلة لإدراج البيانات وحذفها وتعديلها في قاعدة البيانات.
عند استخدام قاعدة بيانات Room
، يمكنك الاستعلام عن قاعدة البيانات من خلال تحديد دوال Kotlin واستدعائها في الرمز البرمجي. يتم ربط دوال Kotlin هذه باستعلامات SQL. يمكنك تحديد عمليات الربط هذه في DAO باستخدام التعليقات التوضيحية، وتنشئ Room
الرمز اللازم.
يمكنك اعتبار DAO على أنّه يحدّد واجهة مخصّصة للوصول إلى قاعدة البيانات.
بالنسبة إلى عمليات قاعدة البيانات الشائعة، توفّر مكتبة Room
تعليقات توضيحية سهلة الاستخدام، مثل @Insert
و@Delete
و@Update
. بالنسبة إلى كل ما عدا ذلك، هناك التعليق التوضيحي @Query
. يمكنك كتابة أي طلب بحث متوافق مع SQLite.
بالإضافة إلى ذلك، أثناء إنشاء طلبات البحث في "استوديو Android"، يتحقّق المحوّل البرمجي من طلبات بحث SQL بحثًا عن أخطاء في البنية.
بالنسبة إلى قاعدة بيانات أداة تتبُّع النوم التي تتضمّن ليالي النوم، يجب أن تتمكّن من إجراء ما يلي:
- أدرِج ليالٍ جديدة.
- تعديل ليلة حالية لتعديل وقت الانتهاء وتقييم الجودة
- الحصول على ليلة معيّنة استنادًا إلى مفتاحها
- الحصول على جميع الليالي، حتى تتمكّن من عرضها
- الحصول على أحدث ليلة
- حذف جميع الإدخالات في قاعدة البيانات
الخطوة 1: إنشاء SleepDatabase DAO
- في حزمة
database
، افتحSleepDatabaseDao.kt
. - لاحظ أنّ
interface
SleepDatabaseDao
تمّت إضافة التعليق التوضيحي@Dao
إليه. يجب إضافة التعليق التوضيحي@Dao
إلى جميع عناصر DAO.
@Dao
interface SleepDatabaseDao {}
- داخل نص الواجهة، أضِف تعليقًا توضيحيًا
@Insert
. أسفل@Insert
، أضِف الدالةinsert()
التي تأخذ مثيلاً من الفئةEntity
SleepNight
كوسيطة.
هذا كل ما في الأمر. سيُنشئRoom
كل الرموز اللازمة لإدراجSleepNight
في قاعدة البيانات. عندما تستدعيinsert()
من رمز Kotlin، ينفِّذRoom
طلب بحث SQL لإدراج العنصر في قاعدة البيانات. (ملاحظة: يمكنك تسمية الدالة بأي اسم تريده).
@Insert
fun insert(night: SleepNight)
- أضِف تعليقًا توضيحيًا
@Update
باستخدام الدالةupdate()
لـSleepNight
واحد. الكيان الذي يتم تعديله هو الكيان الذي يتضمّن المفتاح نفسه الذي تم تمريره. يمكنك تعديل بعض أو كل الخصائص الأخرى للعنصر.
@Update
fun update(night: SleepNight)
لا تتوفّر تعليقات توضيحية سهلة الاستخدام للوظائف المتبقية، لذا عليك استخدام التعليق التوضيحي @Query
وتقديم طلبات بحث SQLite.
- أضِف تعليقًا توضيحيًا
@Query
مع دالةget()
تأخذ وسيطةLong
key
وتعرض قيمةSleepNight
تقبل القيم الخالية. سيظهر لك خطأ بسبب عدم توفّر مَعلمة.
@Query
fun get(key: Long): SleepNight?
- يتم تقديم طلب البحث كمعلَمة سلسلة إلى التعليق التوضيحي. أضِف مَعلمة إلى
@Query
. اجعلهاString
وهي عبارة عن طلب بحث SQLite.
- اختَر جميع الأعمدة من
daily_sleep_quality_table
WHERE
يطابقnightId
الوسيطة :key
.
لاحظ:key
. يمكنك استخدام صيغة النقطتين في طلب البحث للإشارة إلى الوسيطات في الدالة.
("SELECT * from daily_sleep_quality_table WHERE nightId = :key")
- أضِف
@Query
آخر باستخدام الدالةclear()
واستعلام SQLite لـDELETE
كل شيء منdaily_sleep_quality_table
. لا يؤدي طلب البحث هذا إلى حذف الجدول نفسه.
تحذف التعليق التوضيحي@Delete
عنصرًا واحدًا، ويمكنك استخدام@Delete
وتقديم قائمة بالليالي المطلوب حذفها. العيب هو أنّك تحتاج إلى استرداد البيانات أو معرفة ما هو موجود في الجدول. التعليق التوضيحي@Delete
مناسب لحذف إدخالات معيّنة، ولكنّه غير فعّال لمحو جميع الإدخالات من جدول.
@Query("DELETE FROM daily_sleep_quality_table")
fun clear()
- أضِف
@Query
باستخدام الدالةgetTonight()
. اجعلSleepNight
الذي تعرضهgetTonight()
قابلاً للقيم الخالية، حتى تتمكّن الدالة من التعامل مع الحالة التي يكون فيها الجدول فارغًا. (يكون الجدول فارغًا في البداية وبعد محو البيانات).
للحصول على "الليلة" من قاعدة البيانات، اكتب طلب بحث SQLite يعرض العنصر الأول من قائمة النتائج مرتّبة حسبnightId
بترتيب تنازلي. استخدِمLIMIT 1
لعرض عنصر واحد فقط.
@Query("SELECT * FROM daily_sleep_quality_table ORDER BY nightId DESC LIMIT 1")
fun getTonight(): SleepNight?
- أضِف
@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>>
- على الرغم من أنّك لن ترى أي تغييرات ظاهرة، شغِّل تطبيقك للتأكّد من عدم حدوث أي أخطاء.
في هذه المهمة، ستنشئ قاعدة بيانات Room
تستخدم Entity
وDAO اللذين أنشأتهما في المهمة السابقة.
عليك إنشاء فئة مجرّدة لحامل قاعدة البيانات، مع إضافة التعليق التوضيحي @Database
. تتضمّن هذه الفئة طريقة واحدة تنشئ إما مثيلاً لقاعدة البيانات إذا لم تكن قاعدة البيانات متوفّرة، أو تعرض مرجعًا لقاعدة بيانات حالية.
يتطلّب الحصول على قاعدة بيانات Room
بعض الخطوات، لذا إليك العملية العامة قبل البدء في استخدام الرمز:
- أنشئ
public abstract
صفًاextends RoomDatabase
. هذه الفئة هي بمثابة حامل قاعدة البيانات. الفئة مجرّدة، لأنّRoom
تنشئ التنفيذ نيابةً عنك. - إضافة تعليقات توضيحية إلى الصف باستخدام
@Database
في الوسيطات، حدِّد العناصر لقاعدة البيانات واضبط رقم الإصدار. - داخل عنصر
companion
، حدِّد طريقة أو سمة مجرّدة تعرضSleepDatabaseDao
. سينشئRoom
نص الرسالة نيابةً عنك. - تحتاج إلى نسخة واحدة فقط من قاعدة بيانات
Room
للتطبيق بأكمله، لذا اجعلRoomDatabase
عنصرًا فرديًا. - استخدِم أداة إنشاء قواعد البيانات في
Room
لإنشاء قاعدة البيانات فقط إذا لم تكن موجودة. بخلاف ذلك، أعِد قاعدة البيانات الحالية.
الخطوة 1: إنشاء قاعدة البيانات
- في حزمة
database
، افتحSleepDatabase.kt
. - في الملف، أنشئ فئة
abstract
باسمSleepDatabase
تتضمّنRoomDatabase
.
أضِف تعليقًا توضيحيًا إلى الفئة باستخدام@Database
.
@Database()
abstract class SleepDatabase : RoomDatabase() {}
- سيظهر لك خطأ بشأن الكيانات ومعلَمات الإصدار غير المتوفّرة. تتطلّب التعليق التوضيحي
@Database
عدة وسيطات، حتى يتمكّنRoom
من إنشاء قاعدة البيانات.
- قدِّم
SleepNight
كعنصر واحد فقط مع قائمةentities
. - اضبط قيمة
version
على1
. عند تغيير المخطط، عليك زيادة رقم الإصدار. - اضبط
exportSchema
علىfalse
، وذلك لكي لا يتم الاحتفاظ بنسخ احتياطية من سجلّ إصدار المخطط.
entities = [SleepNight::class], version = 1, exportSchema = false
- يجب أن تكون قاعدة البيانات على دراية بالمنظمة اللامركزية المستقلة. داخل نص الفئة، عرِّف قيمة مجرّدة تعرض
SleepDatabaseDao
. يمكنك امتلاك عدة منظمات مستقلة لامركزية.
abstract val sleepDatabaseDao: SleepDatabaseDao
- أسفل ذلك، حدِّد عنصر
companion
. يسمح العنصر المرافق للعملاء بالوصول إلى طرق إنشاء قاعدة البيانات أو الحصول عليها بدون إنشاء مثيل للفئة. بما أنّ الغرض الوحيد من هذه الفئة هو توفير قاعدة بيانات، ليس هناك أي سبب لإنشائها.
companion object {}
- داخل العنصر
companion
، عرِّف المتغيّر الخاص القابل للتصغيرINSTANCE
لقاعدة البيانات واضبط قيمته الأولية علىnull
. سيحتفظ المتغيّرINSTANCE
بإشارة إلى قاعدة البيانات بعد إنشائها. يساعدك ذلك في تجنُّب فتح اتصالات متكررة بقاعدة البيانات، وهو أمر مكلف.
إضافة تعليق توضيحي إلى INSTANCE
باستخدام @Volatile
لن يتم تخزين قيمة متغيّر متطاير مؤقتًا أبدًا، وسيتم إجراء جميع عمليات الكتابة والقراءة من الذاكرة الرئيسية وإليها. يساعد ذلك في التأكّد من أنّ قيمة INSTANCE
تكون دائمًا محدّثة ومتطابقة مع جميع سلاسل التنفيذ. وهذا يعني أنّ التغييرات التي يتم إجراؤها بواسطة سلسلة محادثات واحدة على INSTANCE
تكون مرئية لجميع سلاسل المحادثات الأخرى على الفور، ولا يحدث أن تقوم سلسلتا محادثات مثلاً بتعديل الكيان نفسه في ذاكرة التخزين المؤقت، ما قد يؤدي إلى حدوث مشكلة.
@Volatile
private var INSTANCE: SleepDatabase? = null
- ضمن
INSTANCE
، وداخل العنصرcompanion
، حدِّد طريقةgetInstance()
تتضمّن المَعلمةContext
التي يحتاجها منشئ قاعدة البيانات. إرجاع نوعSleepDatabase
سيظهر لك خطأ لأنّgetInstance()
لم يعرض أي نتائج بعد.
fun getInstance(context: Context): SleepDatabase {}
- داخل
getInstance()
، أضِف مربّعsynchronized{}
. مرِّرthis
حتى تتمكّن من الوصول إلى السياق.
يمكن أن تطلب سلاسل متعددة مثيلاً لقاعدة البيانات في الوقت نفسه، ما يؤدي إلى إنشاء قاعدتَي بيانات بدلاً من واحدة. من غير المحتمل حدوث هذه المشكلة في هذا التطبيق النموذجي، ولكن من المحتمل حدوثها في تطبيق أكثر تعقيدًا. يؤدي تضمين الرمز للحصول على قاعدة البيانات فيsynchronized
إلى إمكانية دخول سلسلة تنفيذ واحدة فقط في كل مرة إلى مجموعة الرموز هذه، ما يضمن عدم تهيئة قاعدة البيانات إلا مرة واحدة.
synchronized(this) {}
- داخل الكتلة المتزامنة، انسخ القيمة الحالية
INSTANCE
إلى المتغيّر المحليinstance
. يتم ذلك للاستفادة من التحويل الذكي، الذي لا يتوفّر إلا للمتغيرات المحلية.
var instance = INSTANCE
- داخل كتلة
synchronized
،return instance
في نهاية كتلةsynchronized
تجاهل خطأ عدم تطابق نوع الإرجاع، فلن يتم إرجاع قيمة فارغة بعد الانتهاء.
return instance
- أضِف عبارة
if
فوق عبارةreturn
للتحقّق مما إذا كانتinstance
فارغة، أي أنّه لم يتم إنشاء قاعدة بيانات بعد.
if (instance == null) {}
- إذا كانت قيمة
instance
هيnull
، استخدِم أداة إنشاء قاعدة البيانات للحصول على قاعدة بيانات. في نص عبارةif
، استدعِRoom.databaseBuilder
وقدِّم السياق الذي مرّرته وفئة قاعدة البيانات واسمًا لقاعدة البيانات،sleep_history_database
. لإزالة الخطأ، عليك إضافة استراتيجية نقل بيانات وbuild()
في الخطوات التالية.
instance = Room.databaseBuilder(
context.applicationContext,
SleepDatabase::class.java,
"sleep_history_database")
- أضِف استراتيجية الترحيل المطلوبة إلى أداة الإنشاء. استخدِم
.fallbackToDestructiveMigration()
.
في العادة، عليك تقديم عنصر نقل بيانات مع استراتيجية نقل بيانات عند تغيير المخطط. عنصر النقل هو عنصر يحدّد كيفية نقل جميع الصفوف التي تتضمّن المخطط القديم وتحويلها إلى صفوف في المخطط الجديد، وذلك لضمان عدم فقدان أي بيانات. نقل البيانات ليس موضوع هذا الدرس التطبيقي. الحلّ البسيط هو إتلاف قاعدة البيانات وإعادة إنشائها، ما يعني فقدان البيانات.
.fallbackToDestructiveMigration()
- أخيرًا، اتّصِل بالرقم
.build()
.
.build()
- عيِّن
INSTANCE = instance
كخطوة نهائية داخل عبارةif
.
INSTANCE = instance
- يجب أن تبدو التعليمات البرمجية النهائية على النحو التالي:
@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
}
}
}
}
- إنشاء التعليمات البرمجية وتشغيلها
أصبح لديك الآن جميع اللبنات الأساسية للعمل مع قاعدة بيانات Room
. يتم تجميع هذا الرمز وتشغيله، ولكن ليس لديك طريقة لمعرفة ما إذا كان يعمل بالفعل. لذا، هذا هو الوقت المناسب لإضافة بعض الاختبارات الأساسية.
الخطوة 2: اختبار SleepDatabase
في هذه الخطوة، يمكنك إجراء الاختبارات المتوفّرة للتحقّق من أنّ قاعدة البيانات تعمل. يساعد ذلك في ضمان عمل قاعدة البيانات قبل إنشاء أي عناصر عليها. الاختبارات المقدَّمة أساسية. بالنسبة إلى تطبيق جاهز للنشر، عليك استخدام جميع الدوال وطلبات البحث في جميع عناصر الوصول إلى البيانات.
يحتوي التطبيق الأوّلي على مجلد androidTest. يحتوي مجلد androidTest هذا على اختبارات وحدة تتضمّن أدوات Android، وهي طريقة رائعة للقول إنّ الاختبارات تحتاج إلى إطار عمل Android، لذا عليك تشغيل الاختبارات على جهاز فعلي أو افتراضي. بالطبع، يمكنك أيضًا إنشاء اختبارات وحدات خالصة وتنفيذها بدون استخدام إطار عمل Android.
- في "استوديو Android"، افتح الملف SleepDatabaseTest في المجلد androidTest.
- لإزالة التعليق من الرمز، حدِّد كل الرمز الذي تمّت إضافة تعليق إليه واضغط على اختصار لوحة المفاتيح
Cmd+/
أوControl+/
. - ألقِ نظرة على الملف.
إليك شرح سريع لرمز الاختبار، لأنّه جزء آخر من الرمز يمكنك إعادة استخدامه:
SleepDabaseTest
هو صف تجريبي.- تحدّد التعليق التوضيحي
@RunWith
مشغّل الاختبار، وهو البرنامج الذي يضبط الاختبارات وينفّذها. - أثناء عملية الإعداد، يتم تنفيذ الدالة التي تمّت إضافة التعليق التوضيحي
@Before
إليها، وتنشئSleepDatabase
في الذاكرة معSleepDatabaseDao
. يشير مصطلح "في الذاكرة" إلى أنّ قاعدة البيانات هذه لا يتم حفظها في نظام الملفات وسيتم حذفها بعد إجراء الاختبارات. - عند إنشاء قاعدة البيانات في الذاكرة، يستدعي الرمز أيضًا طريقة أخرى خاصة بالاختبار، وهي
allowMainThreadQueries
. يظهر لك خطأ تلقائيًا إذا حاولت تنفيذ طلبات بحث على سلسلة التعليمات الرئيسية. تتيح لك هذه الطريقة إجراء اختبارات على سلسلة التعليمات الرئيسية، وهو ما يجب أن تفعله أثناء الاختبار فقط. - في طريقة اختبار تمّت إضافة التعليق التوضيحي
@Test
إليها، يمكنك إنشاءSleepNight
وإدراجه واسترداده، والتأكّد من أنّها متطابقة. في حال حدوث أي خطأ، يجب طرح استثناء. في اختبار حقيقي، ستتوفّر لك طرق@Test
متعددة. - عند الانتهاء من الاختبار، يتم تنفيذ الدالة التي تمّت إضافة التعليق التوضيحي
@After
إليها لإغلاق قاعدة البيانات.
- انقر بزر الماوس الأيمن على ملف الاختبار في لوحة المشروع (Project) واختَر تشغيل SleepDatabaseTest (Run 'SleepDatabaseTest').
- بعد تشغيل الاختبارات، تحقَّق في لوحة SleepDatabaseTest من اجتياز جميع الاختبارات.
بما أنّ جميع الاختبارات قد اجتازت، يمكنك الآن معرفة عدة أمور:
- يتم إنشاء قاعدة البيانات بشكل صحيح.
- يمكنك إدراج
SleepNight
في قاعدة البيانات. - يمكنك استعادة
SleepNight
. - يحتوي
SleepNight
على القيمة الصحيحة للجودة.
مشروع "استوديو Android": TrackMySleepQualityRoomAndTesting
عند اختبار قاعدة بيانات، عليك استخدام جميع الطرق المحدّدة في DAO. لإكمال الاختبار، أضِف الاختبارات ونفِّذها لتجربة طرق DAO الأخرى.
- عرِّف جداولك كفئات بيانات مزوّدة بالتعليق التوضيحي
@Entity
. حدِّد السمات التي تمّت إضافة التعليقات التوضيحية إليها باستخدام@ColumnInfo
كأعمدة في الجداول. - حدِّد كائن الوصول إلى البيانات (DAO) كواجهة مزوّدة بالتعليق التوضيحي
@Dao
. تعمل فئة DAO على ربط دوال Kotlin باستعلامات قاعدة البيانات. - استخدِم التعليقات التوضيحية لتحديد وظائف
@Insert
و@Delete
و@Update
. - استخدِم التعليق التوضيحي
@Query
مع سلسلة طلب بحث SQLite كمعلَمة لأي طلبات بحث أخرى. - أنشئ فئة مجرّدة تحتوي على دالة
getInstance()
تعرض قاعدة بيانات. - استخدِم الاختبارات المزوّدة بأدوات لتأكيد عمل قاعدة البيانات وكائن الوصول إلى البيانات (DAO) على النحو المتوقّع. يمكنك استخدام الاختبارات المتوفّرة كنموذج.
دورة Udacity التدريبية:
مستندات مطوّري تطبيقات Android:
RoomDatabase
Database
(التعليقات التوضيحية)- يمكنك استخدام طلبات البحث الأولية مع
Room
Roomdatabase.Builder
- التدريب على الاختبار
SQLiteDatabase
صفDao
Room
مكتبة الثبات
المستندات والمقالات الأخرى:
يسرد هذا القسم مهامًا منزلية محتملة للطلاب الذين يعملون على هذا الدرس التطبيقي العملي كجزء من دورة تدريبية يقودها مدرّب. على المعلّم تنفيذ ما يلي:
- حدِّد واجبًا منزليًا إذا لزم الأمر.
- توضيح كيفية إرسال الواجبات المنزلية للطلاب
- وضع درجات للواجبات المنزلية
يمكن للمدرّبين استخدام هذه الاقتراحات بالقدر الذي يريدونه، ويجب ألا يترددوا في تكليف الطلاب بأي واجبات منزلية أخرى يرونها مناسبة.
إذا كنت تعمل على هذا الدرس العملي بنفسك، يمكنك استخدام مهام الواجب المنزلي هذه لاختبار معلوماتك.
الإجابة عن هذه الأسئلة
السؤال 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
على عنصر وصول إلى البيانات واحد فقط. - لتحديد فئة كقاعدة بيانات
Room
، اجعلها فئة فرعية منRoomDatabase
وأضِف إليها التعليق التوضيحي@Database
.
السؤال 4
أيّ من التعليقات التوضيحية التالية يمكنك استخدامها في واجهة @Dao
؟ يُرجى اختيار كل ما ينطبق.
@Get
@Update
@Insert
@Query
السؤال 5
كيف يمكنك التأكّد من أنّ قاعدة البيانات تعمل؟ يُرجى اختيار جميع الإجابات المناسبة.
- كتابة اختبارات مزوّدة بأدوات
- واصِل كتابة التطبيق وتشغيله إلى أن يعرض البيانات.
- استبدِل استدعاءات الطرق في واجهة DAO باستدعاءات الطرق المكافئة في الفئة
Entity
. - نفِّذ الدالة
verifyDatabase()
التي توفّرها المكتبةRoom
.
الانتقال إلى الدرس التالي:
للحصول على روابط تؤدي إلى دروس تطبيقية أخرى في هذه الدورة التدريبية، اطّلِع على الصفحة المقصودة الخاصة بالدروس التطبيقية حول أساسيات Android Kotlin.