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

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

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

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

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

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

لتحديد العمليات التي سيتم إنهاؤها عند انخفاض سعة الذاكرة، يضع Android كل عملية في تسلسل هرمي للأهمية بناءً على المكونات التي تعمل فيها وحالة هذه المكونات. حسب الأهمية، أنواع العمليات هذه هي:

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

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

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

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

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

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

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

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

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

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

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

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

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

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