أثناء تنقّل المستخدم في تطبيقك وخارجه ثم العودة إليه، تنتقل مثيلات Activity
في تطبيقك خلال حالات مختلفة في مراحل نشاطها.
يوفر الصف Activity عددًا من عمليات معاودة الاتصال التي تتيح للنشاط معرفة
وقت تغيير الحالة أو أنّ النظام بصدد إنشاء نشاط أو إيقافه أو استئنافه
أو إتلاف العملية التي يتضمّنها النشاط.
يمكنك ضمن طرق الاستدعاء في إحدى مراحل النشاط تحديد طريقة عمل نشاطك عندما يخرج المستخدم منه ثم يعود إليه. على سبيل المثال، إذا كنت بصدد إنشاء مشغّل فيديو للبث المباشر، يمكنك إيقاف الفيديو مؤقتًا وإنهاء اتصال الشبكة عندما ينتقل المستخدم إلى تطبيق آخر. وعندما يعود المستخدم، يمكنك إعادة الاتصال بالشبكة والسماح له باستئناف الفيديو من الموضع نفسه.
تتيح لك كل دالة ردّ تنفيذ عمل محدّد مناسب لتغيير معيّن في الحالة. يؤدي تنفيذ المهام المناسبة في الوقت المناسب والتعامل مع عمليات الانتقال بشكل صحيح إلى جعل تطبيقك أكثر فعالية وأداءً. على سبيل المثال، يمكن أن يساعد التنفيذ الجيد لعمليات معاودة الاتصال بدورة الحياة تطبيقك في تجنُّب ما يلي:
- تعطُّل التطبيق إذا تلقّى المستخدم مكالمة هاتفية أو بدّل إلى تطبيق آخر أثناء استخدام تطبيقك
- استهلاك موارد النظام القيّمة عندما لا يكون المستخدم نشطًا
- فقدان مستوى تقدّم المستخدم إذا غادر تطبيقك وعاد إليه في وقت لاحق
- تعطُّل التطبيق أو فقدان تقدّم المستخدم عند تدوير الشاشة بين الوضعَين الأفقي والرأسي
يوضّح هذا المستند مراحل النشاط بالتفصيل. يبدأ المستند بوصف نموذج دورة الحياة. بعد ذلك، يشرح كلًّا من دوال الرجوع: ما يحدث داخليًا أثناء تنفيذها وما عليك تنفيذه خلالها.
ثم يقدّم نبذة موجزة عن العلاقة بين حالة النشاط ومدى تأثّر العملية بإيقاف النظام لها. وأخيرًا، يناقش هذا القسم عدة مواضيع ذات صلة بعمليات الانتقال بين حالات النشاط.
للحصول على معلومات حول التعامل مع دورات الحياة، بما في ذلك إرشادات حول أفضل الممارسات، يمكنك الاطّلاع على دورة الحياة في Jetpack Compose وحفظ حالات واجهة المستخدم. للتعرّف على كيفية تصميم تطبيق قوي وعالي الجودة باستخدام الأنشطة مع مكوّنات البنية، يمكنك الاطّلاع على دليل بنية التطبيق.
مفاهيم مراحل النشاط
للتنقّل بين مراحل دورة حياة النشاط، يوفّر الصف Activity مجموعة أساسية من ستّ عمليات ردّ اتصال: onCreate وonStart وonResume وonPause وonStop وonDestroy. يستدعي النظام كلّ دالة من دوال الاستدعاء هذه عندما ينتقل النشاط إلى حالة جديدة.
يعرض الشكل 1 تمثيلاً مرئيًا لهذا النموذج.
عندما يبدأ المستخدم في مغادرة النشاط، يستدعي النظام طرقًا لتفكيك النشاط. في بعض الحالات، يتم إيقاف النشاط جزئيًا فقط ويظل موجودًا في الذاكرة، مثلما يحدث عندما ينتقل المستخدم إلى تطبيق آخر. وفي هذه الحالات، يمكن أن يعود النشاط إلى المقدّمة.
إذا عاد المستخدم إلى النشاط، سيتم استئنافه من حيث توقّف. باستثناء بعض الحالات، يتم حظر التطبيقات من بدء الأنشطة عند تشغيلها في الخلفية.
يعتمد احتمال إيقاف النظام لعملية معيّنة، بالإضافة إلى الأنشطة التي تتضمّنها، على حالة النشاط في ذلك الوقت. لمزيد من المعلومات حول العلاقة بين الحالة ومدى التعرّض للإخلاء، يُرجى الاطّلاع على القسم حول حالة النشاط والإخلاء من الذاكرة.
استنادًا إلى مدى تعقيد نشاطك، من المحتمل ألا تحتاج إلى تنفيذ جميع طرق مراحل النشاط. ومع ذلك، من المهم فهم كل منها وتنفيذ تلك التي تجعل تطبيقك يتصرف بالطريقة التي يتوقعها المستخدمون.
Compose وLifecycle
في Compose، تجنَّب وضع منطق النشاط التجاري أو إعداد المراقب اليدوي مباشرةً ضمن عمليات معاودة الاتصال الخاصة بالنشاط، مثل onStart أو onResume. بدلاً من ذلك، استخدِم التأثيرات التي تراعي دورة الحياة وعناصر المراقبة التي تراعي الحالة والتي تتوافق تلقائيًا مع ظهور واجهة المستخدم على الشاشة.
- مجموعة البيانات التي تراعي مراحل النشاط: استخدِم
collectAsStateWithLifecycleلاستهلاك التدفقات منViewModel. تبدأ واجهة برمجة التطبيقات هذه في جمع البيانات تلقائيًا عندما تنتقل واجهة المستخدم إلى الحالة "بدأ"، وتتوقف عندما تنتقل إلى الخلفية، ما يمنع استهلاك الموارد بلا داعٍ. بعد جمع التدفق كحالة، يمكنك استخدامLifecycleEffectsلتشغيل الرمز البرمجي عند وقوع حدث يتم في مراحل النشاط. - تسلسل المنطق: باستخدام واجهات برمجة التطبيقات هذه، تتفاعل واجهة المستخدم مع حالة مراحل النشاط بشكل طبيعي من خلال شجرة التركيب، ما يضمن عدم تنفيذ منطق النشاط التجاري إلا عندما يتفاعل المستخدم بنشاط مع المكوّن.
لمزيد من المعلومات حول Compose ومراحل النشاط، يمكنك الاطّلاع على مراحل النشاط في Jetpack Compose.
عمليات معاودة الاتصال في إحدى مراحل النشاط
يقدّم هذا القسم معلومات مفاهيمية ومعلومات حول التنفيذ بشأن طرق معاودة الاتصال المستخدَمة خلال دورة حياة النشاط.
تنتمي بعض الإجراءات إلى طرق مراحل النشاط. ومع ذلك، ضَع الرمز الذي ينفّذ إجراءات أحد المكوّنات التابعة في المكوّن نفسه بدلاً من طريقة دورة حياة النشاط. لتحقيق ذلك، عليك جعل المكوّن التابع متوافقًا مع مراحل النشاط. للتعرّف على كيفية جعل المكوّنات التابعة متوافقة مع مراحل النشاط، راجِع مراحل النشاط في Jetpack Compose.
onCreate
يجب تنفيذ دالة الاستدعاء هذه التي يتم تشغيلها عندما ينشئ النظام النشاط لأول مرة. عند إنشاء نشاط، ينتقل إلى الحالة تم الإنشاء. في طريقة
onCreate، نفِّذ منطق بدء تشغيل التطبيق الأساسي الذي يحدث
مرة واحدة فقط طوال مدة نشاط التطبيق.
على سبيل المثال، قد يؤدي تنفيذ onCreate إلى ربط البيانات بالقوائم، وربط النشاط ViewModel، وإنشاء بعض المتغيرات ذات النطاق على مستوى الفئة. تتلقّى هذه الطريقة المَعلمة savedInstanceState، وهي عبارة عن كائن Bundle يحتوي على حالة النشاط التي تم حفظها سابقًا. إذا لم يسبق أن كان النشاط موجودًا، ستكون قيمة العنصر Bundle فارغة.
إذا كان لديك مكوّن متوافق مع مراحل النشاط ومرتبط بمراحل نشاط تطبيقك، سيتلقّى الحدث ON_CREATE. يتم استدعاء الطريقة التي تمّت إضافة التعليق التوضيحي @OnLifecycleEvent إليها لكي يتمكّن المكوّن المتوافق مع مراحل النشاط من تنفيذ أي رمز إعداد يحتاج إليه للحالة التي تم إنشاؤها.
يوضّح المثال التالي كيفية دمج دالة Text قابلة للإنشاء في نشاط أساسي:
class ExampleActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { // In here, we can call composables! MaterialTheme { Greeting(name = "compose") } } } } @Composable fun Greeting(name: String) { Text(text = "Hello $name!") }
لا يبقى نشاطك في الحالة "تم الإنشاء". بعد انتهاء تنفيذ طريقة onCreate،
يدخل النشاط في حالة بدء ويستدعي النظام الطريقتَين
onStart وonResume على التوالي.
onStart
عندما ينتقل النشاط إلى الحالة Started، يستدعي النظام onStart.
يؤدي هذا الاستدعاء إلى إظهار النشاط للمستخدم أثناء استعداد التطبيق
لإدخال النشاط إلى المقدّمة وجعله تفاعليًا. على سبيل المثال، يتم في هذه الطريقة تهيئة الرمز الذي يحافظ على واجهة المستخدم.
عندما ينتقل النشاط إلى الحالة Started، يتلقّى أي مكوّن متوافق مع مراحل النشاط ومرتبط بمراحل نشاط النشاط الحدث ON_START.
تكتمل طريقة onStart بسرعة، وكما هو الحال مع الحالة Created، لا يظل النشاط في الحالة Started. بعد انتهاء عملية الاستدعاء هذه، ينتقل النشاط إلى الحالة مستأنف ويستدعي النظام الطريقة onResume.
onResume
عندما ينتقل النشاط إلى الحالة "مستأنف"، يظهر في المقدّمة، ويستدعي النظام دالة رد الاتصال onResume. هذه هي الحالة التي يتفاعل فيها التطبيق مع المستخدم. يبقى التطبيق في هذه الحالة إلى أن يحدث شيء يؤدي إلى إيقاف التركيز على التطبيق، مثل تلقّي الجهاز مكالمة هاتفية أو انتقال المستخدم إلى نشاط آخر أو إيقاف شاشة الجهاز.
عندما ينتقل النشاط إلى الحالة "تمت استعادة النشاط"، يتلقّى أي مكوّن متوافق مع مراحل النشاط ومرتبط بمراحل نشاط النشاط الحدث ON_RESUME. وهنا يمكن لمكوّنات دورة الحياة تفعيل أي وظيفة يجب تشغيلها أثناء ظهور المكوّن في المقدّمة، مثل بدء معاينة الكاميرا.
عند حدوث حدث مقاطِع، ينتقل النشاط إلى الحالة متوقّف مؤقتًا ويستدعي النظام دالة معاودة الاتصال onPause.
إذا عاد النشاط إلى الحالة "مستأنف" من الحالة "متوقف مؤقتًا"، سيستدعي النظام الطريقة onResume مرة أخرى. لهذا السبب، عليك تنفيذ onResume من أجل تهيئة المكوّنات التي يتم تحريرها أثناء onPause وتنفيذ أي عمليات تهيئة أخرى يجب أن تحدث في كل مرة ينتقل فيها النشاط إلى الحالة "مستأنف".
في ما يلي مثال على مكوّن متوافق مع مراحل النشاط يصل إلى الكاميرا عندما يتلقّى المكوّن الحدث ON_RESUME:
class CameraComponent : LifecycleObserver {
...
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
fun initializeCamera() {
if (camera == null) {
getCamera()
}
}
...
}
يعمل الرمز السابق على تهيئة الكاميرا بمجرد أن يتلقّى LifecycleObserver الحدث ON_RESUME. في وضع النوافذ المتعددة، قد يظهر نشاطك بالكامل حتى عندما يكون في الحالة "متوقف مؤقتًا". على سبيل المثال، عندما يكون التطبيق في وضع النوافذ المتعددة وينقر المستخدم على النافذة التي لا تحتوي على نشاطك، ينتقل نشاطك إلى الحالة "متوقف مؤقتًا".
إذا أردت أن تكون الكاميرا نشطة فقط عندما يكون التطبيق في حالة "استئناف" (مرئيًا ونشطًا في المقدّمة)، عليك تهيئة الكاميرا بعد حدث ON_RESUME الموضّح سابقًا. إذا أردت إبقاء الكاميرا نشطة أثناء إيقاف النشاط مؤقتًا مع إبقائه مرئيًا، مثلاً في وضع النوافذ المتعددة، عليك تهيئة الكاميرا بعد حدث ON_START.
ومع ذلك، قد يؤدي إبقاء الكاميرا نشطة أثناء إيقاف نشاطك مؤقتًا إلى حظر وصول تطبيق آخر تم استئنافه في وضع النوافذ المتعددة إلى الكاميرا. في بعض الأحيان، يكون من الضروري إبقاء الكاميرا نشطة أثناء إيقاف نشاطك مؤقتًا، ولكن قد يؤدي ذلك إلى تدهور تجربة المستخدم بشكل عام.
لهذا السبب، يجب التفكير مليًا في الوقت الأنسب للتحكّم في موارد النظام المشترَكة خلال مراحل النشاط في سياق وضع النوافذ المتعددة. لمزيد من المعلومات حول إتاحة وضع النوافذ المتعددة، يُرجى الاطّلاع على إتاحة وضع النوافذ المتعددة.
بغض النظر عن حدث الإنشاء الذي تختار إجراء عملية تهيئة فيه، احرص على استخدام حدث يتم في مراحل النشاط المناسب لإتاحة المورد. إذا بدأت في تنفيذ إجراء ما بعد حدث ON_START، عليك إيقافه أو إنهاءه بعد حدث ON_STOP. إذا أجريت عملية التهيئة بعد حدث ON_RESUME، عليك إجراء عملية الإفراج بعد حدث ON_PAUSE.
يضع مقتطف الرمز البرمجي السابق رمز تهيئة الكاميرا في أحد المكوّنات المتوافقة مع مراحل النشاط. يمكنك بدلاً من ذلك وضع هذا الرمز مباشرةً في عمليات معاودة الاتصال بدورة حياة النشاط، مثل onStart وonStop، ولكن لا ننصحك بذلك. تتيح إضافة هذه المنطق إلى مكوّن مستقل ومدرك لدورة الحياة إعادة استخدام المكوّن في أنشطة متعددة بدون الحاجة إلى تكرار الرمز. للتعرّف على كيفية إنشاء مكوّن متوافق مع مراحل النشاط، يمكنك الاطّلاع على مراحل النشاط في Jetpack Compose.
onPause
يستدعي النظام هذه الطريقة كأول إشارة إلى أنّ المستخدم سيغادر نشاطك، مع أنّ ذلك لا يعني دائمًا أنّه سيتم إيقاف النشاط. تشير هذه الحالة إلى أنّ النشاط لم يعُد في المقدّمة، ولكنّه يظل مرئيًا إذا كان المستخدم في وضع النوافذ المتعددة. هناك عدة أسباب محتملة لدخول نشاط إلى هذه الحالة:
- يؤدي حدث يعطّل تنفيذ التطبيق، كما هو موضّح في القسم حول
الدالة
onResume، إلى إيقاف النشاط الحالي مؤقتًا. وهذه هي الحالة الأكثر شيوعًا. - في وضع النوافذ المتعددة، يركّز النظام على تطبيق واحد فقط في كل مرة، ويوقف جميع التطبيقات الأخرى مؤقتًا.
- عند فتح نشاط جديد شبه شفاف، مثل مربّع حوار، يتم إيقاف النشاط الذي يغطيه مؤقتًا. ويظل النشاط متوقفًا مؤقتًا طالما أنّه مرئي جزئيًا ولكن ليس في المقدّمة.
عندما ينتقل نشاط إلى الحالة "متوقّف مؤقتًا"، يتلقّى أي مكوّن متوافق مع مراحل النشاط ومرتبط بدورة حياة النشاط الحدث ON_PAUSE. وهنا يمكن لمكوّنات دورة الحياة إيقاف أي وظيفة لا يلزم تشغيلها عندما لا يكون المكوّن في المقدّمة، مثل إيقاف معاينة الكاميرا.
استخدِم طريقة onPause لإيقاف العمليات التي لا يمكن مواصلتها مؤقتًا أو تعديلها،
أو التي قد تتم مواصلتها بشكل معتدل، بينما تكون Activity في حالة "متوقّف مؤقتًا"،
والتي تتوقّع استئنافها قريبًا.
يمكنك أيضًا استخدام طريقة onPause لإتاحة موارد النظام أو مقابض أجهزة الاستشعار (مثل نظام تحديد المواقع العالمي (GPS)) أو أي موارد تؤثر في عمر البطارية عندما يكون نشاطك في حالة "متوقف مؤقتًا" ولا يحتاج إليها المستخدم.
ومع ذلك، كما هو موضّح في القسم حول onResume، قد يظل النشاط المتوقّف مؤقتًا مرئيًا بالكامل إذا كان التطبيق في وضع النوافذ المتعددة. ننصحك باستخدام
onStop بدلاً من onPause لإتاحة أو تعديل الموارد والعمليات ذات الصلة بواجهة المستخدم بشكل كامل
بهدف توفير دعم أفضل لوضع النوافذ المتعددة.
في ما يلي مثال على LifecycleObserver يتفاعل مع الحدث ON_PAUSE، وهو نظير لمثال الحدث ON_RESUME السابق، حيث يتم تحرير الكاميرا التي يتم تهيئتها بعد تلقّي الحدث ON_RESUME:
class CameraComponent : LifecycleObserver {
...
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
fun releaseCamera() {
camera?.release()
camera = null
}
...
}
يضع هذا المثال رمز تحرير الكاميرا بعد أن يتلقّى LifecycleObserver الحدث ON_PAUSE.
يكون تنفيذ onPause موجزًا جدًا ولا يوفّر بالضرورة وقتًا كافيًا لتنفيذ عمليات الحفظ. لهذا السبب، لا تستخدِم onPause لحفظ بيانات التطبيق أو بيانات المستخدم أو إجراء طلبات عبر الشبكة أو تنفيذ معاملات قاعدة البيانات.
وقد لا يكتمل هذا العمل قبل اكتمال الطريقة.
بدلاً من ذلك، نفِّذ عمليات إيقاف التشغيل التي تتطلّب تحميلًا كبيرًا خلال onStop. لمزيد من المعلومات حول العمليات المناسبة التي يمكن تنفيذها أثناء onStop، يُرجى الاطّلاع على القسم التالي. لمزيد من المعلومات حول حفظ البيانات، راجِع القسم حول حفظ الحالة واستعادتها.
لا يعني إكمال طريقة onPause أنّ النشاط سيخرج من حالة Paused. بل يظل النشاط في هذه الحالة إلى أن يتم استئنافه أو يصبح غير مرئي تمامًا للمستخدم. إذا تم استئناف النشاط، سيستدعي النظام مرة أخرى الدالة onResume.
إذا انتقل النشاط من الحالة "متوقف مؤقتًا" إلى الحالة "مستأنف"، سيحتفظ النظام بنسخة Activity في الذاكرة، وسيستدعي هذه النسخة عندما يستدعي النظام onResume. في هذه الحالة، لن تحتاج إلى إعادة تهيئة المكوّنات التي تم إنشاؤها خلال أي من طرق معاودة الاتصال التي تؤدي إلى حالة Resumed. إذا أصبح النشاط غير مرئي تمامًا، سيطلب النظام onStop.
onStop
عندما لا يعود نشاطك مرئيًا للمستخدم، ينتقل إلى حالة متوقف، ويستدعي النظام عملية رد الاتصال onStop. يمكن أن يحدث ذلك عندما يغطّي نشاط تم إطلاقه حديثًا الشاشة بأكملها. يستدعي النظام أيضًا onStop
عندما ينتهي النشاط من التشغيل ويوشك على الإنهاء.
عندما ينتقل النشاط إلى الحالة "متوقف"، يتلقّى أي مكوّن متوافق مع دورة الحياة ومرتبط بدورة حياة النشاط الحدث ON_STOP. وهنا يمكن لمكوّنات دورة الحياة إيقاف أي وظيفة لا يلزم تشغيلها عندما لا يكون المكوّن مرئيًا على الشاشة.
في طريقة onStop، يمكنك تحرير أو تعديل الموارد غير اللازمة عندما لا يكون التطبيق مرئيًا للمستخدم. على سبيل المثال، قد يوقف تطبيقك مؤقتًا الرسوم المتحركة أو ينتقل من تحديثات الموقع الجغرافي الدقيقة إلى تحديثات الموقع الجغرافي التقريبية. يؤدي استخدام onStop
بدلاً من onPause إلى استمرار العمل المرتبط بواجهة المستخدم، حتى عندما يشاهد المستخدم نشاطك في وضع النوافذ المتعددة.
استخدِم أيضًا onStop لتنفيذ عمليات إيقاف التشغيل التي تتطلّب قدرًا كبيرًا نسبيًا من وحدة المعالجة المركزية. على سبيل المثال، إذا لم تتمكّن من العثور على وقت أفضل لحفظ المعلومات في قاعدة بيانات، يمكنك إجراء ذلك خلال onStop. يوضّح المثال التالي عملية تنفيذ
onStop التي تحفظ محتوى مسودة ملاحظة في مساحة تخزين ثابتة:
override fun onStop() {
super.onStop()
// Delegate the save operation to the ViewModel, which handles the
// background thread operations (e.g., using Kotlin Coroutines and Room).
noteViewModel.saveDraft()
}
عندما ينتقل نشاطك إلى الحالة "متوقف"، يظل العنصر Activity مقيمًا في الذاكرة، أي يحتفظ بجميع معلومات الحالة والأعضاء، ولكنّه لا يكون مرتبطًا بمدير النوافذ. وعند استئناف النشاط، يتم استرجاع هذه المعلومات.
من الحالة "متوقّف"، يعود النشاط إما للتفاعل مع المستخدم أو ينتهي تشغيل النشاط ويختفي. إذا عاد النشاط، يستدعي النظام onRestart. إذا انتهى تنفيذ Activity، يستدعي النظام onDestroy.
onDestroy
يتم استدعاء onDestroy قبل إيقاف النشاط. يستدعي النظام
وظيفة معاودة الاتصال هذه لأحد السببَين التاليَين:
- ينتهي النشاط إما لأنّ المستخدم أغلق النشاط بالكامل أو لأنّه تم استدعاء
finishفي النشاط. - يؤدي النظام إلى إتلاف النشاط مؤقتًا بسبب تغيير في الإعدادات، مثل تدوير الجهاز أو الانتقال إلى وضع النوافذ المتعددة.
عندما ينتقل النشاط إلى الحالة "تم إتلافه"، يتلقّى أي مكوّن متوافق مع مراحل النشاط مرتبط بدورة حياة النشاط الحدث ON_DESTROY. وهذا هو المكان الذي يمكن فيه لمكوّنات دورة الحياة تنظيف أي بيانات تحتاج إليها قبل إتلاف Activity.
بدلاً من وضع منطق في Activity لتحديد سبب إتلافه، استخدِم عنصر ViewModel لتضمين بيانات العرض ذات الصلة في Activity. إذا تمت إعادة إنشاء Activity بسبب تغيير في الإعدادات، ليس على ViewModel اتّخاذ أي إجراء، لأنّه يتم الاحتفاظ به ومنحه إلى مثيل Activity التالي.
إذا لم تتم إعادة إنشاء Activity، سيتم استدعاء الطريقة onCleared في ViewModel، حيث يمكنها تنظيف أي بيانات تحتاج إليها قبل إيقافها. يمكنك التمييز بين هاتين الحالتين باستخدام الطريقة isFinishing.
إذا كان النشاط سينتهي، فإنّ onDestroy هو آخر ردّ اتصال لدورة الحياة يتلقّاه النشاط. إذا تم استدعاء onDestroy نتيجةً لتغيير في الإعدادات، ينشئ النظام على الفور مثيلاً جديدًا للنشاط ثم يستدعي onCreate في هذا المثيل الجديد بالإعدادات الجديدة.
يؤدي استدعاء onDestroy إلى تحرير جميع الموارد التي لم يتم تحريرها بواسطة عمليات الاستدعاء السابقة، مثل onStop.
حالة النشاط وإزالته من الذاكرة
يوقف النظام العمليات عندما يحتاج إلى توفير مساحة في ذاكرة الوصول العشوائي. يعتمد احتمال أن يوقف النظام عملية معيّنة على حالة العملية في ذلك الوقت. تعتمد حالة العملية بدورها على حالة النشاط الذي يتم تشغيله في العملية. يعرض الجدول 1 الارتباطات بين حالة العملية وحالة النشاط واحتمالية أن يوقف النظام العملية. لا ينطبق هذا الجدول إلا إذا كان أحد العمليات لا يشغّل أنواعًا أخرى من مكوّنات التطبيق.
احتمالية التعرض للقتل |
حالة العملية |
حالة النشاط النهائية |
الأولوية الأدنى |
التطبيق في المقدّمة (الذي يتم التركيز عليه أو سيتم التركيز عليه) |
تم استئناف العملية |
منخفضة |
مرئي (بدون تركيز) |
بدأ/متوقف مؤقتًا |
جودة أعلى |
الخلفية (غير مرئية) |
تم إيقافها |
الأعلى |
فارغة |
مُتلَف |
الجدول 1: العلاقة بين مراحل نشاط العملية وحالة النشاط
لا يوقف النظام نشاطًا بشكل مباشر لإخلاء مساحة في الذاكرة. بدلاً من ذلك، يتم إيقاف العملية التي يتم تشغيل النشاط فيها، ما يؤدي إلى إيقاف النشاط وكل ما يتم تشغيله في العملية. للتعرّف على كيفية الحفاظ على حالة واجهة المستخدم الخاصة بنشاطك واستعادتها عند إيقاف العملية نهائيًا التي بدأها النظام، راجِع القسم حول حفظ الحالة واستعادتها.
يمكن للمستخدم أيضًا إيقاف عملية باستخدام "إدارة التطبيقات" ضمن "الإعدادات" لإيقاف التطبيق المعنيّ.
لمزيد من المعلومات حول العمليات، اطّلِع على نظرة عامة على العمليات وسلاسل التنفيذ.
حفظ حالة واجهة المستخدم المؤقتة واستعادتها
يتوقّع المستخدم أن تظل حالة واجهة المستخدم للنشاط كما هي أثناء إجراء تغيير في الإعدادات، مثل التدوير أو التبديل إلى وضع النوافذ المتعددة. ومع ذلك، يحذف النظام النشاط تلقائيًا عند حدوث تغيير في الإعدادات، ما يؤدي إلى محو أي حالة لواجهة المستخدم مخزّنة في مثيل النشاط.
وبالمثل، يتوقّع المستخدم أن تظل حالة واجهة المستخدم كما هي إذا انتقل مؤقتًا من تطبيقك إلى تطبيق آخر ثم عاد إلى تطبيقك لاحقًا. ومع ذلك، يمكن للنظام إيقاف عملية تطبيقك أثناء عدم نشاط المستخدم وإيقاف نشاطك.
عندما تؤدي قيود النظام إلى إيقاف النشاط، احتفِظ بحالة واجهة المستخدم المؤقتة للمستخدم باستخدام مزيج من ViewModel (للتعامل مع منطق الأنشطة المعقّدة وحالة الشاشة) وواجهة برمجة التطبيقات rememberSaveable في Jetpack Compose (للتعامل مع حالة واجهة المستخدم البسيطة) و/أو التخزين المحلي. لمزيد من المعلومات حول توقعات المستخدمين مقارنةً بسلوك النظام وكيفية الحفاظ على بيانات حالة واجهة المستخدم المعقّدة على أفضل نحو عند إيقاف النشاط وإيقاف العملية نهائيًا من قِبل النظام، اطّلِع على حفظ حالات واجهة المستخدم.
rememberSaveable تنجو تلقائيًا من تغييرات الإعدادات وعمليات إيقاف العملية نهائيًا التي يبدأها النظام من خلال تجميع الحالة في الخلفية، ما يوفّر تجربة سلسة بدون الحاجة إلى رمز نموذجي على مستوى النشاط.
حالة الجهاز الافتراضي
هناك بعض السيناريوهات التي يتم فيها إيقاف نشاطك بسبب السلوك العادي للتطبيق، مثل عندما يضغط المستخدم على زر الرجوع أو عندما يشير نشاطك إلى إيقافه من خلال استدعاء الطريقة finish.
عندما يتم إتلاف نشاطك لأنّ المستخدم يضغط على "رجوع" أو ينتهي النشاط من تلقاء نفسه، يختفي مفهوم كل من النظام والمستخدم لمثيل Activity هذا إلى الأبد. في هذه السيناريوهات، تتطابق توقعات المستخدم مع سلوك النظام، ولن تحتاج إلى اتّخاذ أي إجراءات إضافية.
ومع ذلك، إذا أوقف النظام النشاط بسبب قيود النظام (مثل تغيير الإعدادات أو ضغط الذاكرة)، سيتذكّر النظام أنّه كان موجودًا، على الرغم من أنّ مثيل Activity الفعلي قد تم إيقافه. إذا حاول المستخدم الرجوع إلى النشاط، ينشئ النظام مثيلاً جديدًا لهذا النشاط باستخدام مجموعة من البيانات المحفوظة التي تصف حالة النشاط عند إيقافه.
تُعرف البيانات المحفوظة التي يستخدمها النظام لاستعادة الحالة السابقة باسم حالة المثيل. في الأساس، هي مجموعة من أزواج المفاتيح والقيم. يستخدم النظام تلقائيًا حالة المثيل لحفظ المعلومات الأساسية حول تصميم واجهة المستخدم، مثل إدخال نص المستخدم أو مواضع التمرير.
يمكنك الاستفادة من سلوك النظام هذا باستخدام rememberSaveable. في حال تم إيقاف مثيل النشاط وإعادة إنشائه، تتم تلقائيًا استعادة أي حالة لواجهة المستخدم مضمّنة في rememberSaveable، بدون أن تحتاج إلى كتابة أي رمز إضافي على مستوى النشاط.
ومع ذلك، من المحتمل أن يتضمّن نشاطك معلومات حالة أكثر تعقيدًا تريد استعادتها، مثل بيانات المستخدم أو ردود الشبكة أو متغيرات الأعضاء التي تتتبّع مستوى تقدّم المستخدم. لا تناسب آلية حالة المثيل (وبالتالي rememberSaveable) الاحتفاظ بكمية كبيرة من البيانات، لأنّها تتطلّب النشر على نحو متسلسِل على سلسلة التعليمات الرئيسية وتستهلك ذاكرة عملية النظام.
للحفاظ على كمية أكبر من البيانات، اتّبِع نهجًا مشتركًا باستخدام مساحة التخزين المحلية الثابتة وفئة ViewModel ونقل حالة Compose، كما هو موضّح في حفظ حالات واجهة المستخدم.
حفظ حالة واجهة المستخدم البسيطة والخفيفة باستخدام rememberSaveable
عندما يبدأ نشاطك في التوقّف، يستعد النظام لحفظ معلومات الحالة في حزمة حالة مثيل. للاستفادة من سلوك النظام هذا، يمكنك استخدام rememberSaveable مباشرةً داخل الدوال القابلة للإنشاء.
rememberSaveable يحفظ حالة واجهة المستخدم المؤقتة ويعيدها تلقائيًا، مثل
إدخال نص المستخدم أو مواضع التمرير، وذلك عند إعادة إنشاء النشاط.
لحفظ معلومات الحالة المخصّصة والخفيفة (مثل مستوى تقدّم المستخدم في إحدى الألعاب)، عليك تعريف الحالة باستخدام rememberSaveable. يتولّى إطار عمل Compose عملية النشر على نحو متسلسِل إلى حزمة حالة المثيل في الخلفية:
var userTypedQuery by rememberSaveable(typedQuery, stateSaver = TextFieldValue.Saver) { mutableStateOf( TextFieldValue(text = typedQuery, selection = TextRange(typedQuery.length)) ) }
لحفظ البيانات الثابتة، مثل الإعدادات المفضَّلة للمستخدم أو بيانات قاعدة البيانات، عليك الاستفادة من الفرص المناسبة عندما يكون نشاطك في المقدّمة. إذا لم تتوفر فرصة من هذا النوع، احفظ البيانات الثابتة أثناء تنفيذ طريقة onStop.
استعادة حالة واجهة مستخدم النشاط باستخدام حالة المثيل المحفوظة
عند إعادة إنشاء نشاط بعد إتلافه سابقًا، تتم استعادة الحالة تلقائيًا. عند استخدام rememberSaveable، لن تحتاج إلى كتابة أي منطق استعادة صريح أو التحقّق من حِزم فارغة أو إلغاء معاودة الاتصال بالنشاط. يعمل الرمز الذي يبدأ حالتك ويحفظها أيضًا على استعادتها بسلاسة عند إعادة فتح النشاط:
var userTypedQuery by rememberSaveable(typedQuery, stateSaver = TextFieldValue.Saver) { mutableStateOf( TextFieldValue(text = typedQuery, selection = TextRange(typedQuery.length)) ) }
الأنشطة والتنقّل
من المحتمل أن ينتقل التطبيق بين الشاشات عدة مرات خلال فترة استخدامه،
مثلما يحدث عندما ينقر المستخدم على زر الرجوع في الجهاز أو يختار وجهة جديدة. تستخدِم تطبيقات Android الحديثة عادةً بنية نشاط واحد.
بدلاً من بدء Activity جديد لكل شاشة، يستضيف تطبيقك Activity واحدًا ويستخدم المكوّن Navigation لتبديل الشاشات القابلة للإنشاء داخل هذا النشاط.
لمعرفة كيفية تنفيذ عملية تنقّل حديثة تستند إلى Compose، راجِع دليل مكتبة Navigation 3 في Jetpack Compose.
بدء نشاط من نشاط آخر
قد يحتاج أحد الأنشطة إلى بدء نشاط آخر في مرحلة ما. تظهر هذه الحاجة، على سبيل المثال، عندما يحتاج تطبيق إلى الانتقال من الشاشة الحالية إلى شاشة جديدة.
استنادًا إلى ما إذا كان نشاطك يتطلّب الحصول على نتيجة من النشاط الجديد الذي سيتم البدء فيه، يمكنك بدء النشاط الجديد باستخدام الطريقة startActivity أو الطريقة startActivityForResult. في كلتا الحالتين، عليك تمرير عنصر Intent.
يحدّد العنصر Intent النشاط المطلوب بدءه بالضبط أو يصف نوع الإجراء المطلوب تنفيذه. يختار النظام النشاط المناسب لك، والذي يمكن أن يكون حتى من تطبيق مختلف. يمكن أن يحمل العنصر
Intent أيضًا كميات صغيرة من البيانات لاستخدامها في النشاط
الذي تم بدؤه. لمزيد من المعلومات عن الفئة Intent، راجِع الأهداف وفلاتر الأهداف.
startActivity
إذا كان النشاط الذي تم بدؤه حديثًا لا يحتاج إلى عرض نتيجة، يمكن للنشاط الحالي بدؤه من خلال استدعاء الطريقة startActivity.
عند العمل داخل تطبيقك، غالبًا ما تحتاج إلى تشغيل نشاط معروف. على سبيل المثال، يوضّح مقتطف الرمز التالي كيفية تشغيل نشاط باسم SignInActivity.
val context = LocalContext.current
Button(onClick = {
val intent = Intent(context, SignInActivity::class.java)
context.startActivity(intent)
}) {
Text("Sign In")
}
بدء أنشطة خارجية
في حين أنّ Navigation يتولّى عملية التنقّل داخل التطبيق، سيحتاج
Activity أحيانًا إلى بدء أنشطة أخرى. يحدث ذلك عادةً عندما تريد الاستفادة من تطبيق خارجي لتنفيذ إجراء معيّن، مثل فتح متصفّح ويب أو إرسال رسالة إلكترونية أو التقاط صورة.
لتحقيق ذلك، يمكنك استخدام عنصر Intent لوصف نوع الإجراء الذي تريد تنفيذه، ويطلق النظام النشاط المناسب من تطبيق آخر.
على سبيل المثال، إذا أردت السماح للمستخدم بإرسال رسالة إلكترونية، يمكنك إنشاء النية التالية:
val intent = Intent(Intent.ACTION_SEND).apply {
putExtra(Intent.EXTRA_EMAIL, recipientArray)
}
startActivity(intent)
إذا كنت بحاجة إلى تشغيل نشاط خارجي والحصول على نتيجة (مثل الطلب من تطبيق الكاميرا التقاط صورة وإرجاعها)، استخدِم Activity واجهات برمجة التطبيقات الحديثة للنتائج بدلاً من معاودة الاتصال startActivityForResult المتوقّفة نهائيًا.
تنسيق الأنشطة
عندما يبدأ نشاط ما نشاطًا آخر، يخضع كلاهما لعمليات انتقال دورة الحياة. يتوقف النشاط الأول عن العمل ويدخل في حالة "متوقف مؤقتًا" أو "متوقف"، بينما يتم إنشاء النشاط الآخر. في حال مشاركة هذه الأنشطة للبيانات المحفوظة على القرص أو في مكان آخر، من المهم معرفة أنّه لا يتم إيقاف النشاط الأول تمامًا قبل إنشاء النشاط الثاني. بل تتداخل عملية بدء الثانية مع عملية إيقاف الأولى.
يتم تحديد ترتيب عمليات معاودة الاتصال بدورة الحياة بشكل جيد، خاصةً عندما يكون النشاطان في العملية نفسها، أي التطبيق نفسه، ويبدأ أحدهما الآخر. في ما يلي ترتيب العمليات التي تحدث عند بدء النشاط (أ) للنشاط (ب):
- يتم تنفيذ طريقة
onPauseفي النشاط A. - يتم تنفيذ طرق
onCreateوonStartوonResumeالخاصة بالنشاط B بالتسلسل. يتم الآن التركيز على النشاط B. - إذا لم يعُد النشاط (أ) مرئيًا على الشاشة، سيتم تنفيذ الطريقة
onStopالخاصة به.
يتيح لك تسلسل عمليات معاودة الاتصال بدورة الحياة هذا إدارة عملية نقل المعلومات من نشاط إلى آخر.
مراجع إضافية
لمزيد من المعلومات حول مراحل نشاط التطبيق، اطّلِع على المراجع الإضافية التالية: