العمليات وعمر التطبيق

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

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

من المهم أن يعرف مطوّرو التطبيقات كيفية تأثير مكوّنات التطبيق المختلفة (ولا سيما Activity وService وBroadcastReceiver) في مدّة عملية التطبيق. يمكن أن يؤدي عدم استخدام هذه المكوّنات بشكلٍ صحيح إلى إيقاف النظام لمعالجة التطبيق أثناء تنفيذه عملاً مهمًا.

من الأمثلة الشائعة على أخطاء دورة حياة العملية هو BroadcastReceiver الذي يبدأ سلسلة محادثات عند تلقّي Intent في BroadcastReceiver.onReceive() الطريقة ثم يعود من الدالة. بعد عودته، يصنّف النظامBroadcastReceiver على أنّه لم يعُد نشطًا، وأنّ عملية استضافته لم تعُد مطلوبة، ما لم تكن مكوّنات التطبيق الأخرى نشطة فيه.

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

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

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

  3. تُجري العملية المرئية عملية يدركها المستخدم حاليًا، لذلك سيؤدّي إغلاقها إلى تأثير سلبي ملحوظ في تجربة المستخدم. تُعدّ العملية مرئية في الحالات التالية:
    • يتم تشغيل Activity يظهر للمستخدم على الشاشة ولكن ليس في المقدّمة (تمّت دعوة onPause() method ). قد يحدث ذلك مثلاً إذا تم عرض Activity في المقدّمة على شكل مربّع حوار يتيح رؤية Activity السابق من خلاله.
    • يحتوي على Service يعمل كخدمة تعمل في المقدّمة، من خلال Service.startForeground() (الذي يطلب من النظام التعامل مع الخدمة على أنّها شيء يعلم به المستخدم، أو كما لو كانت مرئية).
    • يستضيف التطبيق خدمة يستخدمها النظام لميزة معيّنة يعرفها المستخدم، مثل خلفية حية أو خدمة أسلوب إدخال.

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

  4. عملية الخدمة هي عملية تمتلك Service تم بدء تشغيلها باستخدام الأسلوب startService(). على الرغم من أنّه لا يمكن للمستخدم رؤية هذه العمليات مباشرةً، إلا أنّها تُجري بشكل عام إجراءات تهمّه (مثل تحميل أو تنزيل بيانات الشبكة في الخلفية)، لذلك يحافظ النظام دائمًا على تشغيل هذه العمليات ما لم تكن الذاكرة غير كافية للاحتفاظ بجميع العمليات التي تعمل في المقدّمة والعمليات المرئية.

    قد يتم خفض شأن الخدمات التي تعمل منذ فترة طويلة (مثل 30 دقيقة أو أكثر) لإدراجها في القائمة المخزّنة مؤقتًا.

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

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

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

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

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

    بدءًا من Android 13، قد تحصل عملية التطبيق على وقت تنفيذ محدود أو لا تحصل على أي وقت تنفيذ إلى أن تدخل إحدى حالات دورة الحياة النشطة أعلاه.

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

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

قد تزيد أيضًا أولوية العملية استنادًا إلى التبعيات الأخرى التي ترتبط بها العملية. على سبيل المثال، إذا كانت العملية "أ" مرتبطة بملف تعريف Service باستخدام العلامة Context.BIND_AUTO_CREATE أو تستخدم ملف تعريف ContentProvider في العملية "ب"، يكون تصنيف العملية "ب" دائمًا مهمًا على الأقل بقدر تصنيف العملية "أ".