يشكّل هذا الدرس التطبيقي جزءًا من الدورة التدريبية لأساسيات Android Kotlin. ستحصل على أقصى قيمة ممكنة من هذه الدورة التدريبية إذا كنت تستخدم الدروس التطبيقية حول الترميز بشكل متسلسل. يتم إدراج جميع الدروس التطبيقية حول ترميز الدورات التدريبية في الصفحة المقصودة لدروس الترميز Android Kotlin Fundamentals.
المقدّمة
في الدرس التطبيقي الأخير حول الترميز، تعرّفت على مراحل نشاط Activity
وFragment
واستكشِفت الطرق التي تُسمى عند تغيّر حالة مراحل نشاط الأنشطة والأجزاء. في هذا الدرس التطبيقي حول الترميز، يمكنك استكشاف مراحل نشاط النشاط بمزيد من التفصيل. ويمكنك أيضًا الاطّلاع على معلومات عن مكتبة مراحل نشاط Android Jetpack'؛ التي يمكن أن تساعدك على إدارة الأحداث في مراحل النشاط باستخدام رمز أفضل وأكثر سهولة في صيانته.
ما يجب معرفته
- تعريف النشاط وكيفية إنشاء نشاط في تطبيقك
- أساسيات مراحل نشاط
Activity
وFragment
، واستدعاءات الاستدعاء التي تم استدعاؤها عند انتقال نشاط بين الولايات. - كيفية إلغاء طريقتَي معاودة الاتصال بدورة الحياة
onCreate()
وonStop()
لتنفيذ العمليات في أوقات مختلفة في النشاط أو في جزء من مراحل النشاط.
ما ستتعرّف عليه
- كيفية إعداد أجزاء من تطبيقك وبدء تشغيلها وإيقافها في استدعاءات مراحل النشاط.
- طريقة استخدام مكتبة مراحل نشاط Android لإنشاء أداة مراقبة لمراحل النشاط، وتسهيل إدارة النشاط وتقسيم مراحل النشاط.
- كيفية تأثير عمليات إيقاف التشغيل في Android على البيانات في تطبيقك، وكيفية حفظ هذه البيانات واستعادتها تلقائيًا عند إغلاق Android لتطبيقك.
- كيفية إنشاء تغييرات في تدوير الجهاز وإجراء تغييرات أخرى على الإعدادات، كما تؤثر تلك التغييرات في حالات مراحل نشاط التطبيق وتؤثر في حالة تطبيقك.
المهام التي ستنفِّذها
- عدِّل تطبيق DessertClicker لتضمين وظيفة الموقّت، ثم فعِّل هذا الموقّت وتوقفه في أوقات مختلفة في مراحل نشاط النشاط.
- يمكنك تعديل التطبيق لاستخدام مكتبة مراحل نشاط Android، وتحويل الفئة
DessertTimer
إلى أداة مراقبة لمراحل النشاط. - يمكنك إعداد واستخدام Android Debug Bridge (
adb
) لمحاكاة عملية إيقاف التطبيق واستدعاءات دورة الحياة التي تحدث في ذلك الوقت. - يمكنك استخدام طريقة
onSaveInstanceState()
للاحتفاظ ببيانات التطبيق التي قد يتم فقدانها إذا تم إغلاق التطبيق بشكل غير متوقع. يمكنك إضافة رمز لاستعادة تلك البيانات عند بدء تشغيل التطبيق مرة أخرى.
في هذا الدرس التطبيقي حول الترميز، يمكنك التوسّع في تطبيق DessertClicker من الدرس التطبيقي السابق للترميز. ويمكنك إضافة موقّت في الخلفية ثم تحويل التطبيق لاستخدام مكتبة مراحل نشاط Android.
في الدرس التطبيقي السابق حول الترميز، تعلّمت كيفية ملاحظة النشاط وتقسيم مراحل النشاط من خلال إلغاء استدعاءات مراحل نشاط مختلفة، وتسجيل الوقت الذي يستدعي فيه النظام عمليات الاستدعاء هذه. في هذه المهمة، ستطّلع على مثال أكثر تعقيدًا لإدارة مهام مراحل النشاط في تطبيق DessertClicker. ويمكنك استخدام موقّت يطبع بيان السجلّ كل ثانية، يتضمّن عدد الثواني التي تم فيها تشغيله.
الخطوة 1: إعداد DessertTimer
- افتح تطبيق DessertClicker من آخر درس تطبيقي حول الترميز. (يمكنك تنزيل ملفات DessertClickerLogs هنا إذا لم يكن لديك التطبيق).
- في طريقة عرض المشروع، وسِّع j&ava> com.example.android.حلوىclicker وافتح
DessertTimer.kt
. لاحظ أنه تم التعليق على كل الرمز في الوقت الحالي، لذلك لا يتم تشغيله كجزء من التطبيق. - اختَر جميع الرموز في نافذة المحرِّر. اختَر الرمز > التعليق بالتعليق الخطي أو اضغط على
Control+/
(Command+/
على جهاز Mac). لا يعرض هذا الأمر كل الرموز في الملف. (قد يعرض "استوديو Android" أخطاء مرجعية لم يتم حلها إلى أن تُعيد إنشاء التطبيق.) - يُرجى العِلم بأنّ الدورة التدريبية
DessertTimer
تتضمّنstartTimer()
وstopTimer()
، ما يؤدي إلى بدء الموقّت وإيقافه. عندما يكونstartTimer()
قيد التشغيل، يطبع الموقّت رسالة سجلّ كل ثانية، مع إجمالي عدد الثواني التي تم خلالها تشغيل الوقت. ويؤدي استخدام طريقةstopTimer()
بدوره إلى إيقاف الموقّت وكشوفات الحساب.
- فتح
MainActivity.kt
في أعلى الصف الدراسي، تحت المتغيّرdessertsSold
مباشرةً، أضِف متغيّرًا للموقِّت:
private lateinit var dessertTimer : DessertTimer;
- انتقل للأسفل إلى
onCreate()
وأنشئ كائنًا جديدًا علىDessertTimer
، بعد الاتصال بالرقمsetOnClickListener()
مباشرةً:
dessertTimer = DessertTimer()
والآن بعد أن أصبح لديك عنصر موقّت الحلوى، فكِّر في المكان الذي يجب أن تبدأ فيه وإيقاف الموقّت ليتم تشغيله فقط عندما يكون النشاط على الشاشة. ويمكنك الاطّلاع على بعض الخيارات في الخطوات التالية.
الخطوة 2: بدء الموقِّت وإيقافه
يتم استدعاء الطريقة onStart()
قبل أن يصبح النشاط مرئيًا مباشرة. يتم استدعاء طريقة onStop()
بعد أن يصبح النشاط غير مرئي. يبدو أن استدعاءات ردّ الأموال هذه مناسبة كمرشحين مناسبين لبدء الموقّت وإيقافه.
- في الصف
MainActivity
، ابدأ الموقّت في معاودة الاتصال منonStart()
:
override fun onStart() {
super.onStart()
dessertTimer.startTimer()
Timber.i("onStart called")
}
- إيقاف الموقّت في
onStop()
:
override fun onStop() {
super.onStop()
dessertTimer.stopTimer()
Timber.i("onStop Called")
}
- اجمَع التطبيق وشغِّله. في "استوديو Android"، انقر على الجزء Logcat. في مربّع بحث Logcat، أدخِل
dessertclicker
، وستتم الفلترة حسب فئتَيMainActivity
وDessertTimer
. يُرجى العلم بأنّه سيتمّ تشغيل الموقّت فور بدء التشغيل عند بدء تشغيل التطبيق. - انقر على الزر رجوع ولاحظ أن الموقّت يتوقف مرة أخرى. ويتوقّف الموقّت لأنّ كل من النشاط والموقّت الذي يتحكّم فيه قد تم إتلافهما.
- يمكنك استخدام شاشة "التطبيقات الأخيرة" للرجوع إلى التطبيق. لاحظ في Logcat أنّ الموقّت يبدأ من الصفر من جديد.
- انقر على الزر مشاركة. لاحظ في Logcat أن الموقّت لا يزال قيد التشغيل.
- انقر على زر الصفحة الرئيسية. لاحظ في Logcat أن الموقّت يتوقف عن العمل.
- يمكنك استخدام شاشة "التطبيقات الأخيرة" للرجوع إلى التطبيق. لاحظ في Logcat أنّ الموقّت يبدأ مرة أخرى من حيث توقّف.
- في
MainActivity
، الطريقةonStop()
، علِّق المكالمة إلىstopTimer()
. يوضّح التعليق علىstopTimer()
الحالة التي بدأت فيها عملية فيonStart()
، ولكن نسيت إيقافها مرة أخرى فيonStop()
. - اجمَع التطبيق وشغِّله، ثم انقر على زر الشاشة الرئيسية بعد بدء الموقِّت. على الرغم من تشغيل التطبيق في الخلفية، إلا أن الموقّت قيد التشغيل ويستخدم موارد النظام باستمرار. إنّ تشغيل الموقِّت يُعدّ تسرُّبًا إلى الذاكرة للتطبيق، وقد لا يكون سلوكًا مناسبًا.
التصميم العام هو أنه عند إعداد شيء أو معاودة الاتصال به، سيتم إيقاف ذلك أو إزالته في معاودة الاتصال. وبهذه الطريقة، تتجنب تشغيل أي شيء عندما لا تكون هناك حاجة إليه.
- يُرجى إزالة التعليق في السطر
onStop()
الذي يتم فيه إيقاف الموقّت. - يمكنك قص
startTimer()
مكالمة ولصقها منonStart()
إلىonCreate()
. يوضّح هذا التغيير الحالة التي يتم فيها إعداد مورد وبدء تشغيله فيonCreate()
، بدلاً من استخدامonCreate()
لإعداده وonStart()
لبدء تشغيله. - اجمع التطبيق وشغِّله. لاحظ أن الموقّت يبدأ تشغيله كما تتوقع.
- انقر على "الصفحة الرئيسية" لإيقاف التطبيق. ويتوقّف عمل الموقّت كما هو متوقّع.
- يمكنك استخدام شاشة "التطبيقات الأخيرة" للرجوع إلى التطبيق. لاحظ أنّ الموقِّت لا يبدأ مرة أخرى في هذه الحالة، لأنه يتم استدعاء
onCreate()
عند بدء تشغيل التطبيق فقط، ولا يتم استدعاءه عند عودة التطبيق إلى المقدّمة.
نقاط رئيسية يجب تذكّرها:
- عند إعداد مورد في استدعاء دورة الحياة، يمكنك أيضًا إزالة المورد.
- إجراء الإعداد وقطعه بالطرق المقابلة.
- في حال إعداد شيء ما في
onStart()
، يمكنك إيقافه أو تمزقه مرة أخرى خلالonStop()
.
في تطبيق DessertClicker، يسهل عليك معرفة أنّه إذا بدأت الموقّت في onStart()
، عليك إيقاف الموقّت في onStop()
. هناك موقّت واحد فقط، لذا من الصعب تذكّر الموقّت.
في تطبيق أكثر تعقيدًا لنظام التشغيل Android، يمكنك إعداد العديد من العناصر في onStart()
أو onCreate()
، ثم تمويه جميعها في onStop()
أو onDestroy()
. على سبيل المثال، قد تكون لديك صور متحركة أو موسيقى أو أجهزة استشعار أو موقتات تحتاج إلى إعدادها و مساعدة على البدء، ثم البدء والإيقاف. وإذا نسيت ذلك، قد يؤدي ذلك إلى أخطاء ومشاكل.
تعمل مكتبة مراحل النشاط، التي تعد جزءًا من Android Jetpack، على تبسيط هذه المهمة. وتُعد هذه المكتبة مفيدة بشكل خاص في الحالات التي يتعين عليك فيها تتبع العديد من الأجزاء المتحركة، بينما يكون بعضها في حالات مختلفة لمراحل الحياة. تغيّر طريقة عمل المكتبة في مراحل النشاط: توجّه عادةً النشاط أو الجزء مكوّنًا (مثل DessertTimer
) إلى ما يجب فعله عند معاودة الاتصال بدورة الحياة. ولكن عند استخدام مكتبة مراحل النشاط، يتحكم المكوّن نفسه في تغييرات دورة الحياة، ثم ينفّذ الإجراءات اللازمة عند حدوث هذه التغييرات.
هناك ثلاثة أجزاء رئيسية من مكتبة مراحل النشاط:
- مُلاك مراحل النشاط، وهم المكوّنات التي تحتوي على مراحل نشاط (ومن ثم؛)
Activity
وFragment
هما مالكا مراحل النشاط. يستخدم مالكو مراحل النشاط واجهةLifecycleOwner
. - فئة
Lifecycle
التي تحتفظ بالحالة الفعلية لمالك مراحل النشاط وتبدأ الأحداث عند حدوث تغييرات في مراحل النشاط. - أدوات التنبيه في مراحل النشاط، التي ترصد حالة مراحل نشاطها وتؤدي المهام عند تغيّر مراحل النشاط. يستخدم مراقبو مراحل النشاط واجهة
LifecycleObserver
.
في هذه المهمة، يمكنك تحويل تطبيق DessertClicker لاستخدام مكتبة مراحل نشاط Android، والتعرّف على كيفية إدارة المكتبة بسهولة من خلال النشاط على Android وتقسيم مراحل النشاط في مراحل النشاط.
الخطوة 1: تحويل DdesrtTimer إلى LifecycleObserver
تشكّل ملاحظة دورة الحياة جزءًا أساسيًا من مكتبة مراحل النشاط. تتيح الملاحظة للصفوف (مثل DessertTimer
) معرفة الأنشطة أو تجزئة مراحل النشاط، وبدءها والتوقف عن الاستجابة استجابةً للتغييرات التي تطرأ على حالات دورة الحياة هذه. باستخدام أداة مراقبة دورة الحياة، يمكنك إزالة مسؤولية بدء وإيقاف أي أجسام من النشاط وأساليب التجزئة.
- افتَح الصف
DesertTimer.kt
. - تغيير توقيع الفئة
DessertTimer
ليبدو كما يلي:
class DessertTimer(lifecycle: Lifecycle) : LifecycleObserver {
يؤدي تعريف الفئة الجديد هذا إلى أمرين:
- تستخدم هذه الواجهة عنصر
Lifecycle
وهو دورة الحياة التي يراقبها الموقّت. - يستخدم تعريف الفئة واجهة
LifecycleObserver
.
- أسفل المتغيّر
runnable
، أضِف كتلةinit
إلى تعريف الفئة. في الكتلةinit
، استخدِم الطريقةaddObserver()
لربط كائن مراحل النشاط الذي تم تمريره من المالك (النشاط) إلى هذه الفئة (المراقب).
init {
lifecycle.addObserver(this)
}
- يمكنك إضافة تعليقات توضيحية إلى
startTimer()
باستخدام@OnLifecycleEvent annotation
، واستخدام حدث مراحل نشاطON_START
. جميع أحداث مراحل النشاط التي يمكن أن يراقبها مراقب مراحل النشاط في صفLifecycle.Event
.
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun startTimer() {
- نفِّذ الإجراء نفسه المتّبع في
stopTimer()
، باستخدام الحدثON_STOP
:
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun stopTimer()
الخطوة 2: تعديل MainActivity
لقد سبق وأن تم إعداد هذه الفئة من MainActivity
لتكون مالكًا لدورة الحياة من خلال اكتساب الفئة FragmentActivity
على مستوى LifecycleOwner
. ولذلك، ليس عليك اتخاذ أي إجراءات لجعل نشاطك واعيًا. ما عليك سوى تمرير كائن مراحل نشاط النشاط إلى دالّة DessertTimer
.
- فتح
MainActivity
في طريقةonCreate()
، عدِّل إعدادDessertTimer
لتضمينthis.lifecycle
:
dessertTimer = DessertTimer(this.lifecycle)
تحمل الخاصية lifecycle
للنشاط العنصر Lifecycle
الذي يملكه هذا النشاط.
- أزِل المكالمة إلى
startTimer()
فيonCreate()
، والمكالمة إلىstopTimer()
فيonStop()
. ليس عليك إخبارDessertTimer
بما يجب فعله في النشاط بعد الآن، لأنDessertTimer
يراقب الآن مراحل النشاط نفسها ويتم إشعاره تلقائيًا عند تغيّر حالة مراحل النشاط. كل ما عليك فعله في عمليات الاستدعاء هذه هو تسجيل رسالة. - اجمَع التطبيق وشغِّله، وافتح Logcat. ملاحظة: لقد بدأ الموقّت يعمل كما هو متوقع.
- انقر على زر الشاشة الرئيسية لوضع التطبيق في الخلفية. يُرجى العلم بأنّ الموقّت قد توقف عن العمل، كما هو متوقع.
ماذا يحدث لتطبيقك وبياناته إذا أوقف Android تشغيل هذا التطبيق أثناء تشغيله في الخلفية؟ هذه الحافظة الصعبة مهمة.
عندما ينتقل تطبيقك إلى الخلفية، لن يتم إتلافه، وسوف يتوقف فقط وينتظر حتى يعود المستخدم إليه. ولكن من بين المخاوف الرئيسية لنظام التشغيل Android هو الحفاظ على عمل النشاط في المقدمة بسلاسة. على سبيل المثال، إذا كان المستخدم يستخدم تطبيق نظام تحديد المواقع العالمي (GPS) لمساعدته على التقاط الحافلة، من المهم عرض تطبيق نظام تحديد المواقع العالمي (GPS) هذا بسرعة ومواصلة عرض الاتجاهات. من الأقل أهمية استمرار تطبيق DessertClicker الذي قد لا ينظر إليه المستخدم لبضعة أيام، ليتم تشغيله بسلاسة في الخلفية.
ينظّم نظام التشغيل Android تطبيقات الخلفية بحيث يمكن تشغيل التطبيق في المقدّمة بدون مشاكل. مثلاً، يحدّ نظام التشغيل Android من حدّ معالجة التطبيقات التي يتم تشغيلها في الخلفية.
في بعض الأحيان، يوقف Android عملية التطبيق بالكامل، والتي تتضمن كل الأنشطة المرتبطة بالتطبيق. ويُنفِّذ نظام التشغيل Android هذا النوع من عمليات الإيقاف عند الضغط على النظام وعرضة للتأخير البصري، لذا لا يتم تنفيذ عمليات استدعاء أو رمز إضافي في هذه المرحلة. يتم إيقاف تشغيل تطبيقك تلقائيًا، في الخلفية بدون صوت. ولكن بالنسبة إلى المستخدم، لا يبدو أنه تم إغلاق التطبيق. وعندما ينتقل المستخدم مرة أخرى إلى تطبيق تم إيقاف تشغيل نظام التشغيل Android عليه، تتم إعادة تشغيل هذا التطبيق من خلال نظام التشغيل Android.
في هذه المهمة، يمكنك محاكاة إيقاف عملية Android وفحص ما يحدث لتطبيقك عند تشغيله مرة أخرى.
الخطوة 1: استخدام أداة adb لمحاكاة عملية إيقاف التشغيل
Android Debug Bridge (adb
) هو أداة سطر أوامر تتيح لك إرسال تعليمات إلى المحاكيات والأجهزة المتصلة بجهاز الكمبيوتر. في هذه الخطوة، يمكنك استخدام adb
لإغلاق عملية التطبيق ومعرفة ما يحدث عند إغلاق Android لتطبيقك.
- اجمع تطبيقك وشغِّله. انقر على الكب كيك عدة مرات.
- اضغط على زر الشاشة الرئيسية لوضع تطبيقك في الخلفية. تم إيقاف تطبيقك الآن، ويخضع التطبيق للإغلاق إذا احتاج Android إلى الموارد التي يستخدمها التطبيق.
- في "استوديو Android"، انقر على علامة التبويب Terminal (الوحدة الطرفية) لفتح الوحدة الطرفية لسطر الأوامر.
- اكتب
adb
واضغط على "إرجاع".
إذا رأيت أنّ النتائج كثيرة تبدأ بـAndroid Debug Bridge version X.XX.X
وتنتهي بـtags to be used by logcat (see logcat —h
elp، لا مشكلة في ذلك. إذا ظهر لكadb: command not found
، تأكّد من توفّر الأمرadb
في مسار التنفيذ. للحصول على تعليمات، يُرجى الاطّلاع على"إضافة Adb إلى مسار التنفيذ"في فصل"الأدوات المساعدة". - انسخ هذا التعليق والصقه في سطر الأوامر واضغط على Return:
adb shell am kill com.example.android.dessertclicker
يؤدي هذا الأمر إلى إبلاغ أي أجهزة أو أدوات محاكاة متصلة بإيقاف العملية باستخدام اسم حزمة dessertclicker
، ولكن فقط إذا كان التطبيق في الخلفية. بسبب عدم ظهور تطبيقك في الخلفية، لا يظهر أي شيء على شاشة الجهاز أو المحاكي للإشارة إلى أن العملية قد تم إيقافها. في "استوديو Android"، انقر على علامة التبويب تشغيل للاطّلاع على الرسالة التي تفيد بأنّه "تم إنهاء التطبيق". انقر على علامة التبويب Logcat للتأكّد من عدم تنفيذ معاودة الاتصال على onDestroy()
مطلقًا، انتهى نشاطك.
- يمكنك استخدام شاشة الأخيرة للعودة إلى التطبيق. ويظهر تطبيقك مؤخرًا سواء تم وضعه في الخلفية أو توقفه تمامًا. يتم عرض النشاط مجددًا عند استخدام الشاشة الأخيرة للعودة إلى التطبيق. يخضع النشاط لمجموعة كاملة من استدعاءات مراحل نشاط بدء التشغيل، بما في ذلك
onCreate()
. - تجدر الإشارة إلى أنه عند إعادة تشغيل التطبيق، تتم إعادة ضبط &&;&score;quot; (كل من عدد الحلويات التي تم بيعها وإجمالي الدولار) على القيم التلقائية (0). لماذا توقّف نظام التشغيل Android عن تشغيل تطبيقك، لماذا لم يحفظ ذلك؟
عندما يُعيد نظام التشغيل تشغيل تطبيقك نيابةً عنك، يبذل Android قصارى جهده لإعادة ضبط تطبيقك على الحالة التي كان عليه. ويحتفظ نظام التشغيل Android بحالة بعض مشاهداتك ويحفظها في حزمة كلما انتقلت بعيدًا عن النشاط. تتضمّن بعض الأمثلة على البيانات التي يتم حفظها تلقائيًا النص المتوفّر في ملف TextText (طالما أنّه يحتوي على رقم تعريف تم ضبطه في التنسيق) والحزمة الخلفية لنشاطك.
في بعض الأحيان، لا يتعرّف نظام التشغيل Android على جميع بياناتك. على سبيل المثال، إذا كان لديك متغيّر مخصّص مثلrevenue
في تطبيق DessertClicker، لا يعرف نظام التشغيل Android هذه البيانات أو أهميتها بالنسبة إلى نشاطك. وعليك إضافة هذه البيانات إلى الحزمة بنفسك.
الخطوة 2: استخدام onSaveInstanceState() لحفظ بيانات الحزمة
الطريقة onSaveInstanceState()
هي معاودة الاتصال التي تستخدمها لحفظ أي بيانات قد تحتاج إليها إذا كان نظام التشغيل Android يضر بتطبيقك. في مخطط استدعاء دورة الحياة، يتم استدعاء onSaveInstanceState()
بعد إيقاف النشاط. يتم طلب ذلك في كل مرة ينتقل فيها تطبيقك إلى الخلفية.
يمكنك اعتبار طلب onSaveInstanceState()
كإجراء أمني، وهو يمنحك فرصة لحفظ قدر صغير من المعلومات في حِزمة عند خروج نشاطك من المقدّمة. سيحفظ النظام هذه البيانات الآن لأنّه في حال انتظارها حتى يتم إيقافها، قد يكون نظام التشغيل تحت ضغط الموارد. ويضمن حفظ البيانات في كل مرة إتاحة تحديث البيانات في الحزمة لاستعادتها، إذا لزم الأمر.
- في
MainActivity
، يمكنك إلغاء استدعاءonSaveInstanceState()
وإضافة عبارة سجلTimber
.
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
Timber.i("onSaveInstanceState Called")
}
- جمِّع التطبيق وشغِّله، ثم انقر على زر الشاشة الرئيسية لوضعه في الخلفية. لاحِظ أن معاودة الاتصال في
onSaveInstanceState()
تحدث بعدonPause()
وonStop()
: - في أعلى الملف، مباشرةً قبل تعريف الفئة، أضِف الثوابت التالية:
const val KEY_REVENUE = "revenue_key"
const val KEY_DESSERT_SOLD = "dessert_sold_key"
const val KEY_TIMER_SECONDS = "timer_seconds_key"
ستستخدم هذه المفاتيح لحفظ البيانات واستردادها من حزمة حالة المثيل على حد سواء.
- انتقِل للأسفل وصولاً إلى
onSaveInstanceState()
، ولاحظ المعلّمةoutState
، وهي من النوعBundle
.
حزمة تشمل مجموعة من أزواج المفتاح/القيمة، حيث تكون المفاتيح دائمًا عبارة عن سلاسل. يمكنك وضع القيم الأولية، مثل قيمint
وboolean
، في الحزمة.
نظرًا لأن النظام يحتفظ بهذه الحزمة في ذاكرة الوصول العشوائي، فهي أفضل ممارسة للحفاظ على البيانات في الحزمة صغيرة. حجم هذه الحزمة محدود أيضًا، إلا أن الحجم يختلف من جهاز لآخر. بشكل عام، يجب تخزين أقل من 100 ألف، وإلا قد يؤدي ذلك إلى تعطُّل تطبيقك مع ظهور الخطأTransactionTooLargeException
. - في
onSaveInstanceState()
، ضع القيمةrevenue
(عدد صحيح) في الحزمة باستخدام الطريقةputInt()
:
outState.putInt(KEY_REVENUE, revenue)
تستخدم طريقة putInt()
(والطرق المشابهة من الفئة Bundle
، مثل putFloat()
وputString()
، وسيطتين: سلسلة للمفتاح (ثابت KEY_REVENUE
)، والقيمة الفعلية للحفظ.
- كرِّر العملية نفسها مع عدد الحلويات المُباعة، وحالة الموقّت:
outState.putInt(KEY_DESSERT_SOLD, dessertsSold)
outState.putInt(KEY_TIMER_SECONDS, dessertTimer.secondsCount)
الخطوة 3: استخدام onCreate() لاستعادة بيانات الحزمة
- يمكنك الانتقال إلى
onCreate()
وفحص توقيع الطريقة:
override fun onCreate(savedInstanceState: Bundle) {
ويُرجى العِلم أنّ onCreate()
يحصل على Bundle
في كل مرة يتم فيها الاتصال به. عند إعادة تشغيل نشاطك بسبب إيقاف تشغيل العملية، يتم تمرير الحزمة التي حفظتها إلى onCreate()
. إذا بدأ نشاطك من جديد، هذه المجموعة في onCreate()
هي null
. وإذا لم تكن الحزمة null
، هذا يعني أنك تعرف النشاط ضمن نقطة معروفة سابقًا.
- أضف هذا الرمز إلى
onCreate()
، بعد إعدادDessertTimer
:
if (savedInstanceState != null) {
revenue = savedInstanceState.getInt(KEY_REVENUE, 0)
}
يحدد اختبار null
ما إذا كانت هناك بيانات في الحزمة أو ما إذا كانت الحزمة null
، وهذا بدوره يخبرك بما إذا كان التطبيق قد بدأ حديثًا أو قد تمت إعادة إنشاؤه بعد إيقاف التشغيل. يُعتبر هذا الاختبار نمطًا شائعًا لاستعادة البيانات من الحزمة.
يُرجى ملاحظة أن المفتاح الذي استخدمته هنا (KEY_REVENUE
) هو المفتاح نفسه الذي استخدمته لـ putInt()
. للتأكد من أنك تستخدم المفتاح نفسه في كل مرة، من أفضل الممارسات تحديد هذه المفاتيح على أنها ثوابت. يمكنك استخدام getInt()
لإخراج البيانات من الحزمة، تمامًا كما استخدمت putInt()
لإضافة البيانات إلى الحزمة. تستخدم الطريقة getInt()
وسيطتين:
- سلسلة تعمل كمفتاح، على سبيل المثال
"key_revenue"
لقيمة الأرباح. - قيمة تلقائية في حال عدم وجود قيمة لهذا المفتاح في الحزمة
يتم بعد ذلك تعيين العدد الصحيح الذي تحصل عليه من الحزمة إلى المتغير revenue
، وستستخدم واجهة المستخدم هذه القيمة.
- أضِف
getInt()
طريقة لاستعادة عدد الحلويات التي تم بيعها وقيمة الموقّت.
if (savedInstanceState != null) {
revenue = savedInstanceState.getInt(KEY_REVENUE, 0)dessertsSold = savedInstanceState.getInt(KEY_DESSERT_SOLD, 0)
dessertTimer.secondsCount =
savedInstanceState.getInt(KEY_TIMER_SECONDS, 0)
}
- اجمع التطبيق وشغِّله. اضغط على الكعكة خمس مرات على الأقل حتى تتحول إلى كعكة. انقر على زر الشاشة الرئيسية لوضع التطبيق في الخلفية.
- في علامة التبويب Terminal في Android Studio، شغِّل
adb
لإيقاف عملية التطبيق.
adb shell am kill com.example.android.dessertclicker
- يمكنك استخدام شاشة الأخيرة للعودة إلى التطبيق. ويُرجى ملاحظة أن التطبيق سيعود في الوقت الحالي بقيم الأرباح والحلويات المباعة من الحزمة. وَلَكِنْ يَجِبُ أَيْضًا عَرْضْ هَذِهِ الْكَعْكَة الْخَاصَّة بِالْكَبْكْ. وهناك إجراء متبقٍ عليك تنفيذه للتأكد من عودة التطبيق من عملية إيقاف التشغيل مثلما حدث بالضبط.
- في
MainActivity
، ادرس طريقةshowCurrentDessert()
. استخدِم هذه الطريقة لتحديد صورة الحلوى التي يجب عرضها في النشاط استنادًا إلى العدد الحالي من الحلويات المُباعة وقائمة الحلويات في المتغيّرallDesserts
.
for (dessert in allDesserts) {
if (dessertsSold >= dessert.startProductionAmount) {
newDessert = dessert
}
else break
}
تعتمد هذه الطريقة على عدد الحلويات التي تم بيعها لاختيار الصورة المناسبة. ولذلك، لا تحتاج إلى اتخاذ أي إجراء لتخزين مرجع للصورة في الحزمة في onSaveInstanceState()
. في هذه الحزمة، يتم تخزين عدد الحلويات التي تم بيعها.
- في
onCreate()
، في الكتلة التي تستعيد الحالة من الحزمة، اتّصِل بالرقمshowCurrentDessert()
:
.
if (savedInstanceState != null) {
revenue = savedInstanceState.getInt(KEY_REVENUE, 0)
dessertsSold = savedInstanceState.getInt(KEY_DESSERT_SOLD, 0)
dessertTimer.secondsCount =
savedInstanceState.getInt(KEY_TIMER_SECONDS, 0)
showCurrentDessert()
}
- اجمع التطبيق وشغّله ثم شغّله في الخلفية. استخدِم
adb
لإيقاف العملية. يمكنك استخدام الشاشة الأخيرة للعودة إلى التطبيق. لاحظ الآن أنه تمت استعادة كل من قيم الحلويات وإجمالي الأرباح وصورة الحلوى بشكل صحيح.
هناك حالة واحدة أخيرة خاصة في إدارة النشاط ودورة حياة الأجزاء المُهمّة والتي من المهم فهمها: مدى تأثير تغييرات الضبط في دورة حياة الأنشطة والأجزاء.
يحدث تغيير الضبط عندما تتغيّر حالة الجهاز جذريًا، فإنّ أسهل طريقة لإصلاح النظام هي إيقاف النشاط تمامًا وإعادة تشغيله. على سبيل المثال، إذا غيّر المستخدم لغة الجهاز، قد يلزم تغيير التنسيق بالكامل لاستيعاب الاتجاهات النصية المختلفة. وإذا وصّل المستخدم الجهاز بقاعدة إرساء أو أضاف لوحة مفاتيح فعلية، قد يحتاج تنسيق التطبيق إلى الاستفادة من حجم شاشة أو تنسيق مختلف. وإذا تغيّر اتجاه الجهاز، إذا تم تدوير الجهاز من وضع عمودي إلى وضع أفقي أو للخلف، قد تحتاج إلى تغيير التنسيق ليلائم الاتجاه الجديد.
الخطوة 1: التعرّف على تدوير الجهاز واستدعاءات مراحل النشاط
- اجمَع تطبيقك وشغِّله، وافتح Logcat.
- حاوِل تدوير الجهاز أو المحاكي إلى الوضع الأفقي. يمكنك تدوير المحاكي لليسار أو اليمين باستخدام أزرار التدوير أو باستخدام
Control
ومفاتيح الأسهم (Command
ومفاتيح الأسهم على جهاز Mac). - افحص المخرجات في Logcat. فلترة النتائج على
MainActivity
لاحظ أنه عندما يجري الجهاز أو المحاكي تدوير الشاشة، يتصل النظام بجميع عمليات الاستدعاء في مراحل النشاط لإيقاف النشاط. وبعد ذلك، عند إعادة إنشاء النشاط، يستدعي النظام جميع عمليات استدعاء دورة الحياة لبدء النشاط. - في
MainActivity
، يمكنك التعليق على طريقةonSaveInstanceState()
بأكملها. - اجمع تطبيقك وشغِّله مرة أخرى. انقر على الكب كيك عدة مرات، ثم حرِّك الجهاز أو المحاكي. في هذه المرة، عندما يتم تدوير الجهاز ويتم إيقاف النشاط وإعادة إنشائه، يبدأ النشاط بالقيم التلقائية.
عند حدوث تغيير في الضبط، يستخدم نظام التشغيل نفس حزمة الحالة الافتراضية التي تعرَّفت عليها في المهمة السابقة لحفظ حالة التطبيق واستعادتها. وكما هو الحال مع إيقاف المعالجة، يمكنك استخدامonSaveInstanceState()
لوضع بيانات تطبيقك في الحزمة. يمكنك بعد ذلك استعادة البيانات فيonCreate()
، لتجنُّب فقدان بيانات حالة النشاط إذا تم تدوير الجهاز. - في
MainActivity
، عليك إلغاء تعليق طريقةonSaveInstanceState()
وتشغيل التطبيق والنقر على الكب كيك وتدوير التطبيق أو الجهاز. يُرجى ملاحظة في هذا الوقت يتم الاحتفاظ ببيانات الحلوى عبر عرض أنشطة بالتناوب.
مشروع "استوديو Android": DessertClickerFinal
نصائح حول مراحل النشاط
- في حال إعداد إجراء أو بدء عملية استدعاء مراحل النشاط، يمكنك إيقاف هذا الإجراء أو إزالته في معاودة الاتصال. من خلال إيقاف هذا العنصر، تأكد من أنه لا يتم تشغيله عندما لا تكون هناك حاجة إليه. مثلاً، في حال إعداد موقّت في
onStart()
، عليك إيقاف الموقّت بشكل مؤقت أو دائم فيonStop()
. - يمكنك استخدام
onCreate()
للإعداد فقط لأجزاء تطبيقك التي يتم تشغيلها مرة واحدة عند بدء تشغيل التطبيق لأول مرة. استخدِمonStart()
لبدء تشغيل أجزاء التطبيق التي تعمل عند تشغيل التطبيق وفي كل مرة يعود فيها إلى المقدّمة.
مكتبة مراحل النشاط
- استخدِم مكتبة مراحل نشاط Android لنقل عناصر التحكم في مراحل النشاط من النشاط أو الجزء إلى المكوِّن الفعلي الذي يجب أن يكون على دراية بمراحل النشاط.
- مراحل النشاط المالكون هم المكوّنات التي تحتوي على مراحل نشاط (وبالتالي، "own") بما في ذلك
Activity
وFragment
. يستخدم مالكو مراحل النشاط واجهةLifecycleOwner
. - يركّز مراقبو مراحل النشاط على حالة دورة الحياة الحالية ويؤديون المهام عند تغيّر مراحل النشاط. يستخدم مراقبو مراحل النشاط واجهة
LifecycleObserver
. - تحتوي كائنات
Lifecycle
على حالات مراحل النشاط الفعلية، وتؤدي إلى تشغيل الأحداث عندما تتغير مراحل النشاط.
لإنشاء دورة تدريبية مستندة إلى مراحل النشاط:
- استخدام واجهة
LifecycleObserver
في الصفوف التي يجب أن تكون على دراية بمراحل الحياة. - إعداد فئة أداة مراقبة دورة الحياة باستخدام كائن مراحل النشاط من النشاط أو الجزء.
- في فئة أداة مراقبة مراحل النشاط، أضف تعليقات توضيحية إلى الطرق الواعية لمراحل النشاط مع تغيير حالة مراحل نشاطها.
على سبيل المثال، يشير@OnLifecycleEvent(Lifecycle.Event.ON_START)
التعليق التوضيحي إلى أن الطريقة تشاهد حدثonStart
مراحل النشاط.
عمليات قطع الإنترنت وحفظ حالة النشاط
- ينظّم نظام التشغيل Android التطبيقات التي يتم تشغيلها في الخلفية بحيث يمكن تشغيل التطبيق في المقدّمة بدون حدوث أي مشاكل. وتشمل هذه اللوائح تقييد مقدار المعالجة التي يمكن للتطبيقات في الخلفية تنفيذها، وأحيانًا إيقاف تشغيل التطبيقات بأكملها.
- لا يمكن للمستخدم معرفة ما إذا كان النظام قد أوقف تشغيل التطبيق في الخلفية أم لا. لا يزال التطبيق يظهر في الشاشة الأخيرة ويجب إعادة تشغيله بنفس الحالة التي تركه المستخدم بها.
- Android Debug Bridge (
adb
) هو أداة سطر أوامر تتيح لك إرسال تعليمات إلى المحاكيات والأجهزة المتصلة بجهاز الكمبيوتر. يمكنك استخدامadb
لمحاكاة إيقاف تشغيل التطبيقات. - عندما يوقف Android عملية معالجة تطبيقك، لا يتم استدعاء طريقة مراحل نشاط
onDestroy()
. توقف التطبيق.
الحفاظ على النشاط وحالة التقسيم
- عندما ينتقل تطبيقك إلى الخلفية، يتم طلب بيانات التطبيق في حِزمة بعد طلب
onStop()
مباشرةً. ويتم تلقائيًا حفظ بعض بيانات التطبيقات، مثل محتوىEditText
. - تمثّل الحزمة مثيلاً لـ
Bundle
، وهي مجموعة من المفاتيح والقيم. تكون المفاتيح دائمًا سلاسل. - استخدِم رد الاتصال في
onSaveInstanceState()
لحفظ البيانات الأخرى في الحزمة التي تريد الاحتفاظ بها، حتى في حال إيقاف التطبيق تلقائيًا. لإضافة البيانات إلى الحزمة، استخدِم طرق الحزمة التي تبدأ بـput
، مثلputInt()
. - يمكنك استعادة البيانات من الحزمة باستخدام طريقة
onRestoreInstanceState()
، أو بشكل أكثر شيوعًا فيonCreate()
. تتضمّن الطريقةonCreate()
المعلّمةsavedInstanceState
التي تحتوي على الحزمة. - إذا كان المتغيّر
savedInstanceState
يحتوي علىnull
، يكون النشاط قد بدأ بدون حزمة الحالة ولا تتوفّر بيانات الحالة لاستردادها. - لاسترداد البيانات من الحزمة التي تحتوي على مفتاح، استخدِم طرق
Bundle
التي تبدأ بـget
، مثلgetInt()
.
تغييرات الضبط
- يحدث تغيير الضبط عندما تتغيّر حالة الجهاز جذريًا، بحيث إنّ أسهل طريقة لحلّ النظام تتمثّل في إيقاف النشاط وإعادة إنشائه.
- من الأمثلة الأكثر شيوعًا لتغيير الإعداد عندما يُدير المستخدم الجهاز من الوضع الرأسي إلى الوضع الأفقي، أو من الوضع الأفقي إلى الوضع العمودي. يمكن أن يحدث تغيير في الضبط أيضًا عند تغيير لغة الجهاز أو توصيل لوحة مفاتيح الجهاز.
- عند حدوث تغيير في الإعداد، يستدعي نظام التشغيل Android جميع استدعاءات نشاط إيقاف التشغيل. بعد ذلك، سيعيد Android النشاط من البداية، مع إجراء جميع استدعاءات بدء تشغيل مراحل النشاط.
- عندما يغلق Android أحد التطبيقات بسبب تغيير في الإعداد، يعيد هذا التطبيق تشغيل النشاط مع حِزمة الحالة المتاحة لـ
onCreate()
. - كما هو الحال مع إيقاف العملية، يمكنك حفظ حالة التطبيق في الحزمة على
onSaveInstanceState()
.
دورة Udacity:
مستندات مطوّر برامج Android:
- الأنشطة (دليل واجهة برمجة التطبيقات)
Activity
(مرجع واجهة برمجة التطبيقات)- فهم مراحل النشاط
- التعامل مع مراحل النشاط باستخدام مكوّنات الوعي بمراحل الحياة
LifecycleOwner
Lifecycle
LifecycleObserver
onSaveInstanceState()
- تغييرات في التعامل مع الإعدادات
- حفظ حالات واجهة المستخدم
غير ذلك:
- Timber (GitHub)
يسرد هذا القسم المهام الدراسية المحتملة للطلاب الذين يعملون من خلال هذا الدرس التطبيقي حول الترميز في إطار دورة تدريبية يُديرها معلِّم. يجب أن ينفِّذ المعلّم ما يلي:
- يمكنك تخصيص واجب منزلي إذا لزم الأمر.
- التواصل مع الطلاب بشأن كيفية إرسال الواجبات المنزلية
- وضع درجات للواجبات المنزلية.
ويمكن للمعلّمين استخدام هذه الاقتراحات بقدر ما يريدون أو بقدر ما يريدون، ويجب عدم التردد في تخصيص أي واجبات منزلية أخرى مناسبة.
إذا كنت تستخدم هذا الدرس التطبيقي بنفسك، يمكنك استخدام هذه الواجبات المنزلية لاختبار معلوماتك.
تغيير تطبيق
افتح تطبيق DiceRoller من الدرس 1. (يمكنك تنزيل التطبيق من هنا إذا لم يكن مثبتًا لديك). اجمَع التطبيق وشغِّله، وملاحظة أنه في حال تدوير الجهاز، سيتم فقد القيمة الحالية للنرد. نفِّذ onSaveInstanceState()
للاحتفاظ بهذه القيمة في الحزمة، ثم استعِد هذه القيمة في onCreate()
.
الإجابة عن هذه الأسئلة
السؤال 1
يحتوي تطبيقك على محاكاة فيزيائية تتطلب حسابًا مكثفًا لعرضها. بعد ذلك، يتلقى المستخدم مكالمة هاتفية. أيّ عبارة من العبارات التالية تُعتبر صحيحة؟
- أثناء المكالمة الهاتفية، يجب مواصلة رصد مواضع العناصر في محاكاة الفيزياء.
- أثناء المكالمة الهاتفية، يجب أن تتوقف عن حساب مواضع العناصر في محاكاة الفيزياء.
السؤال 2
ما طريقة دورة الحياة التي يجب إلغاءها لإيقاف المحاكاة مؤقتًا عندما لا يظهر التطبيق على الشاشة؟
onDestroy()
onStop()
onPause()
onSaveInstanceState()
السؤال 3
لتحديد دورة حياة دورة تدريبية من خلال مكتبة مراحل نشاط Android، ما الواجهة التي يجب أن ينفذها الصف؟
Lifecycle
LifecycleOwner
Lifecycle.Event
LifecycleObserver
السؤال 4
ما الظروف التي تتلقى فيها طريقة onCreate()
في نشاطك Bundle
مع بيانات بها (أي أن Bundle
ليست null
)؟ قد يتم تطبيق أكثر من إجابة واحدة.
- تتم إعادة تشغيل النشاط بعد تدوير الجهاز.
- يبدأ النشاط من نقطة الصفر.
- يتم استئناف النشاط بعد الرجوع من الخلفية.
- تتم إعادة تشغيل الجهاز.
بدء الدرس التالي:
وللحصول على روابط إلى دروس تطبيقية أخرى حول الترميز في هذه الدورة التدريبية، يُرجى الاطّلاع على الصفحة المقصودة لتطبيق الدروس التطبيقية حول الترميز Kotlin Fundamentals.