مراحل النشاط

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

ضمن طرق الاستدعاء في إحدى مراحل النشاط، يمكنك تحديد سلوك النشاط عندما يغادره المستخدم ثم يعود إليه. على سبيل المثال، إذا كنت تنشئ مشغّل فيديو للبث، يمكنك إيقاف الفيديو مؤقتًا وإنهاء الاتصال بالشبكة عندما ينتقل المستخدم إلى تطبيق آخر. وعندما يعود المستخدم، يمكنك إعادة الاتصال بالشبكة والسماح للمستخدم باستئناف الفيديو من المكان نفسه.

تتيح لك كل دالة استدعاء تنفيذ عمل محدّد مناسب لتغيير معيّن في الحالة. يؤدي تنفيذ العمل المناسب في الوقت المناسب والتعامل مع عمليات الانتقال بشكل صحيح إلى جعل تطبيقك أكثر قوة وأداءً. على سبيل المثال، يمكن أن يساعد التنفيذ الجيد لعمليات الاستدعاء في إحدى مراحل النشاط تطبيقك على تجنُّب ما يلي:

  • يتعطّل التطبيق إذا تلقّى المستخدم مكالمة هاتفية أو انتقل إلى تطبيق آخر أثناء استخدام تطبيقك.
  • استهلاك موارد النظام القيّمة عندما لا يستخدمه المستخدم بشكل نشط.
  • فقدان مستوى تقدّم المستخدم إذا غادر تطبيقك وعاد إليه في وقت لاحق
  • تعطُّل التطبيق أو فقدان تقدُّم المستخدم عند تدوير الشاشة بين الوضعَين الأفقي والعمودي

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

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

للحصول على معلومات حول التعامل مع مراحل النشاط، بما في ذلك إرشادات حول أفضل الممارسات، يُرجى الاطّلاع على مراحل النشاط في Jetpack Compose وحفظ حالات واجهة المستخدم. لمعرفة كيفية تصميم تطبيق قوي بجودة إنتاجية باستخدام الأنشطة مع مكوّنات البنية، يُرجى الاطّلاع على دليل بنية التطبيق.

مفاهيم مراحل النشاط

للتنقّل بين مراحل النشاط، توفّر فئة Activity مجموعة أساسية من ست عمليات استدعاء: onCreate وonStart وonResume وonPause وonStop وonDestroy. يستدعي النظام كلًّا من عمليات الاستدعاء هذه عندما يدخل النشاط في حالة جديدة.

يعرض الشكل 1 تمثيلاً مرئيًا لهذا النموذج.

الشكل 1. صورة توضيحية مبسّطة لمراحل النشاط

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

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

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

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

‫Compose ومراحل النشاط

في 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

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

عندما ينتقل النشاط إلى الحالة "بدأ"، يتلقّى أي مكوّن يتتبّع مراحل النشاط ومرتبط بمراحل النشاط الحدث ON_START.

تكتمل طريقة onStart بسرعة، وكما هو الحال مع حالة "تم الإنشاء"، لا يظل النشاط في حالة "تم البدء". بعد انتهاء عملية الاستدعاء هذه، يدخل النشاط في حالة الاستئناف ويستدعي النظام طريقة 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()
        }
    }
    ...
}

The preceding code initializes the camera once the LifecycleObserver receives the ON_RESUME event. ومع ذلك، في وضع النوافذ المتعددة، قد يكون نشاطك مرئيًا بالكامل حتى عندما يكون في حالة "متوقف مؤقتًا". على سبيل المثال، عندما يكون التطبيق في وضع النوافذ المتعددة وينقر المستخدم على النافذة التي لا تحتوي على نشاطك، ينتقل نشاطك إلى الحالة "متوقف مؤقتًا".

إذا أردت أن تكون الكاميرا نشطة فقط عندما يكون التطبيق في حالة "استئناف" (مرئي ونشط في المقدّمة)، عليك إعداد الكاميرا بعد حدث 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
    }
    ...
}

يضع هذا المثال رمز تحرير الكاميرا بعد تلقّي الحدث ON_PAUSE من قِبل LifecycleObserver.

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

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

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

إذا عاد النشاط من حالة "الإيقاف المؤقت" إلى حالة "الاستئناف"، يحتفظ النظام بمثيل Activity في الذاكرة، ويستدعي هذا المثيل عندما يستدعي النظام onResume. في هذا السيناريو، لن تحتاج إلى إعادة تهيئة المكوّنات التي تم إنشاؤها خلال أي من طرق رد الاتصال التي تؤدي إلى حالة "الاستئناف". إذا أصبح النشاط غير مرئي تمامًا، يستدعي النظام onStop.

onStop

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

عندما ينتقل النشاط إلى الحالة "متوقف"، يتلقّى أي مكوّن مرتبط بمراحل النشاط الحدث ON_STOP. هذا هو المكان الذي يمكن فيه لمكونات مراحل النشاط إيقاف أي وظيفة لا تحتاج إلى التشغيل أثناء عدم ظهور المكوِّن على الشاشة.

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

يمكنك أيضًا استخدام onStop لإجراء عمليات إيقاف تشغيل تستهلك قدرًا كبيرًا من طاقة وحدة المعالجة المركزية (CPU). على سبيل المثال، إذا لم تتمكّن من العثور على وقت أفضل لحفظ المعلومات في قاعدة بيانات، يمكنك إجراء ذلك خلال 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 قبل تدمير النشاط. يستدعي النظام دالة الاستدعاء هذه لأحد السببَين التاليَين:

  1. ينتهي النشاط بسبب رفض المستخدم للنشاط بالكامل أو بسبب استدعاء finish في النشاط.
  2. يُنهي النظام النشاط مؤقتًا بسبب تغيير في الإعدادات، مثل تدوير الجهاز أو الدخول في وضع النوافذ المتعددة.

عندما ينتقل النشاط إلى حالة التدمير، يتلقّى أي مكوّن مرتبط بمراحل النشاط الحدث ON_DESTROY. هنا يمكن لمكوّنات مراحل النشاط تنظيف أي شيء تحتاج إليه قبل تدمير Activity.

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

إذا لم تتم إعادة إنشاء Activity، يتم استدعاء طريقة onCleared في ViewModel، حيث يمكنها تنظيف أي بيانات تحتاج إليها قبل تدميرها. يمكنك التمييز بين هذين السيناريوهين باستخدام طريقة isFinishing.

إذا كان النشاط على وشك الانتهاء، تكون onDestroy هي آخر استدعاء في إحدى مراحل النشاط يتلقّاه النشاط. إذا تم استدعاء onDestroy نتيجة تغيير في الإعدادات، ينشئ النظام على الفور مثيلاً جديدًا للنشاط ثم يستدعي onCreate على هذا المثيل الجديد في الإعدادات الجديدة.

تؤدي دالة الاستدعاء onDestroy إلى تحرير جميع الموارد التي لم يتم تحريرها من خلال دوال الاستدعاء السابقة، مثل onStop.

حالة النشاط والإخراج من الذاكرة

يوقف النظام العمليات عندما يحتاج إلى إخلاء مساحة في ذاكرة RAM. تعتمد احتمالية إنهاء النظام لعملية معينة على حالة العملية في ذلك الوقت. وتعتمد حالة العملية بدورها على حالة النشاط الذي يتم تشغيله في العملية. يوضّح الجدول 1 الارتباطات بين حالة العملية وحالة النشاط واحتمالية إنهاء النظام للعملية. لا ينطبق هذا الجدول إلا إذا كانت العملية لا تشغّل أنواعًا أخرى من مكوّنات التطبيق.

احتمالية القتل

حالة العملية

حالة النشاط النهائية

الأولوية الأدنى

المقدمة (التي يتم التركيز عليها أو على وشك التركيز عليها)

تم استئناف العملية

منخفضة

مرئي (بدون تركيز)

بدأت/متوقفة مؤقتًا

جودة أعلى

الخلفية (غير مرئية)

تم إيقافها

الأعلى

فارغة

مُتلَف

الجدول 1: العلاقة بين مراحل العملية وحالة النشاط

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

يمكن للمستخدم أيضًا إنهاء عملية باستخدام "مدير التطبيقات" (Application Manager) ضمن "الإعدادات" (Settings) لإنهاء التطبيق المعنيّ.

لمزيد من المعلومات حول العمليات، يُرجى الاطّلاع على نظرة عامة على العمليات وسلاسل المحادثات.

حفظ حالة واجهة المستخدم المؤقتة واستعادتها

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

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

عندما تؤدي قيود النظام إلى تدمير النشاط، يمكنك الحفاظ على حالة واجهة المستخدم المؤقتة للمستخدم باستخدام مزيج من ViewModel (لمنطق الأعمال المعقد وحالة الشاشة)، وواجهة برمجة التطبيقات Jetpack Compose rememberSaveable (لحالة واجهة المستخدم الخفيفة)، و/أو مساحة التخزين المحلية. لمزيد من المعلومات حول توقعات المستخدمين مقارنةً بسلوك النظام وأفضل طريقة للحفاظ على بيانات حالة واجهة المستخدم المعقدة في جميع الأنشطة التي يبدأها النظام وإيقاف العملية نهائيًا، يُرجى الاطّلاع على حفظ حالات واجهة المستخدم.

يحتفظ 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 التي تم إيقافها نهائيًا.

تنسيق الأنشطة

عندما يبدأ نشاط نشاطًا آخر، يمر كلاهما بمراحل انتقالية في مراحل النشاط. يتوقف النشاط الأول عن العمل ويدخل في حالة الإيقاف المؤقت أو الإيقاف، بينما يتم إنشاء النشاط الآخر. في حال كانت هذه الأنشطة تشارك البيانات المحفوظة على القرص أو في مكان آخر، من المهم فهم أن النشاط الأول لا يتم إيقافه تمامًا قبل إنشاء النشاط الثاني. بدلاً من ذلك، تتداخل عملية بدء تشغيل الثانية مع عملية إيقاف الأولى.

يتم تحديد ترتيب عمليات الاستدعاء في إحدى مراحل النشاط بشكل جيد، خاصةً عندما يكون النشاطان في العملية نفسها، أي في التطبيق نفسه، ويبدأ أحدهما الآخر. في ما يلي ترتيب العمليات التي تحدث عندما يبدأ النشاط "أ" النشاط "ب":

  1. يتم تنفيذ الإجراء onPause الخاص بالنشاط "أ".
  2. يتم تنفيذ الطرق onCreate وonStart وonResume للنشاط "ب" بالتسلسل. أصبح النشاط "ب" الآن في بؤرة تركيز المستخدم.
  3. إذا لم يعُد النشاط "أ" مرئيًا على الشاشة، يتم تنفيذ طريقة onStop الخاصة به.

يتيح لك هذا التسلسل من عمليات الاستدعاء في إحدى مراحل النشاط إدارة نقل المعلومات من نشاط إلى آخر.

مراجع إضافية

لمزيد من المعلومات حول مراحل النشاط، يُرجى الاطّلاع على المراجع الإضافية التالية:

محتوى المشاهدات