حدود التنفيذ في الخلفية

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

نظرة عامة

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

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

  • قيود الخدمات التي تعمل في الخلفية: عندما يكون التطبيق في وضع السكون، تنطبق قيود على استخدام الخدمات التي تعمل في الخلفية. ولا ينطبق ذلك على خدمات foreground ، التي تكون أكثر وضوحًا للمستخدم.

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

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

قيود الخدمات التي تعمل في الخلفية

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

يميز النظام بين التطبيقات التي تعمل في المستوى الأول والتطبيقات التي تعمل في المستوى الثاني. (يختلف تعريف "الخلفية" لأغراض القيود المفروضة على الخدمات عن التعريف المستخدَم في إدارة الذاكرة، فقد يكون التطبيق في الخلفية في ما يتعلق بإدارة الذاكرة، ولكن في المقدّمة في ما يتعلق بقدرته على تشغيل الخدمات). يُعتبر التطبيق قيد الاستخدام على واجهة الجهاز في أيٍّ من الحالات التالية:

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

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

عندما يكون التطبيق في المقدّمة، يمكنه إنشاء خدمات تعمل في المقدّمة والخلفية وتشغيلها بحرية. عندما ينتقل التطبيق إلى الخلفية، يحصل على فترة تبلغ عدة دقائق يظل خلالها مسموحًا له بإنشاء خدمات واستخدامها. في نهاية هذه الفترة، يُعتبر التطبيق غير نشط. في هذه الحالة، يوقف النظام خدمات التطبيق التي تعمل في الخلفية، تمامًا كما لو كان التطبيق قد استدعى طرق Service.stopSelf() للخدمات.

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

في كثير من الحالات، يمكن لتطبيقك استبدال الخدمات التي تعمل في الخلفية بوظائف JobScheduler. على سبيل المثال، يحتاج تطبيق CoolPhotoApp إلى التحقّق مما إذا كان المستخدم قد تلقّى صورًا مشترَكة من الأصدقاء، حتى إذا لم يكن التطبيق قيد التشغيل في المقدّمة. في السابق، كان التطبيق يستخدم خدمة تعمل في الخلفية كانت تتحقق من مساحة التخزين السحابي للتطبيق. لنقل البيانات إلى Android 8.0 (المستوى 26 من واجهة برمجة التطبيقات)، يستبدل المطوِّر الخدمة التي تعمل في الخلفية بعملية مجدوَلة يتم تشغيلها بانتظام، وتطلب بيانات من الخادم، ثم تتوقف.

قبل الإصدار 8.0 من نظام التشغيل Android، كانت الطريقة المعتادة لإنشاء خدمة تعمل في المقدّمة هي إنشاء خدمة تعمل في الخلفية، ثم ترقية هذه الخدمة إلى المقدّمة. في نظام التشغيل Android 8.0، هناك مشكلة، إذ لا يسمح النظام لتطبيق يعمل في الخلفية بإنشاء خدمة تعمل في الخلفية. لهذا السبب، يقدّم نظام التشغيل Android 8.0 الطريقة الجديدة startForegroundService() لبدء خدمة جديدة في المقدّمة. بعد أن ينشئ النظام الخدمة، يكون لدى التطبيق خمس ثوانٍ للاتّصال بطريقة [startForeground()](/reference/android/app/Service#startForeground(int, android.app.Notification) لعرض الإشعار الجديد الذي يظهر للمستخدم في الخدمة. إذا لم يطلب التطبيق startForeground() خلال الحدّ الزمني، يوقف النظام الخدمة ويعلن أنّ التطبيق يتضمّن خطأ ANR.

القيود المفروضة على البث

إذا سجّل تطبيق لتلقّي أحداث البث، يستهلك جهاز الاستقبال في التطبيق موارد في كل مرة يتم فيها إرسال البث. ويمكن أن يؤدي ذلك إلى حدوث مشاكل إذا سجّلت عددًا كبيرًا جدًا من التطبيقات لتلقّي عمليات البث استنادًا إلى أحداث النظام، إذ يمكن أن يؤدي حدث النظام الذي يؤدي إلى بدء عملية بث إلى استهلاك جميع هذه التطبيقات للموارد بشكلٍ متتابع وبسرعة، ما يؤثر سلبًا في تجربة المستخدم. للحدّ من هذه المشكلة، فرض نظام Android 7.0 (المستوى 24 من واجهة برمجة التطبيقات) قيودًا على عمليات البث، كما هو موضّح في تحسين الأداء في الخلفية. يفرض الإصدار 8.0 من نظام التشغيل Android (المستوى 26 لواجهة برمجة التطبيقات) قيودًا أكثر صرامة.

  • بالنسبة إلى التطبيقات التي تستهدف الإصدار 8.0 من نظام التشغيل Android أو الإصدارات الأحدث، لم يعُد بإمكانها تسجيل مستقبِلات البث لعمليات البث الضمنية في ملف البيان الخاص بها ما لم يكن البث محصورًا بذلك التطبيق تحديدًا. البث الضمني هو نوع من البث الذي لا يستهدف مكوّنًا معيّنًا في أحد التطبيقات. على سبيل المثال، يتم إرسال ACTION_PACKAGE_REPLACED إلى جميع المستمعين المسجّلين في جميع التطبيقات لإعلامهم بأنّه تم استبدال بعض الحِزم على الجهاز. وبما أنّ البث ضمني، لن يتم توجيهه إلى أجهزة الاستقبال المسجّلة في البيان في التطبيقات التي تستهدف الإصدار 8.0 من نظام التشغيل Android أو الإصدارات الأحدث. ACTION_MY_PACKAGE_REPLACED هو أيضًا بث ضمني، ولكن بما أنّه لا يتم إرساله إلا إلى التطبيق الذي تم استبدال ملفه التعريفي، سيتم تسليمه إلى المستلمين المسجّلين في البيان.
  • يمكن للتطبيقات مواصلة التسجيل في أحداث البث الصريح في ملفات البيان الخاصة بها.
  • يمكن للتطبيقات استخدام Context.registerReceiver() أثناء التشغيل لتسجيل جهاز استقبال لأي نوع من البث، سواء كان ضمنيًا أو صريحًا.
  • إنّ عمليات البث التي تتطلّب إذن توقيع معفاة من هذا القيد، لأنّ عمليات البث هذه لا يتم إرسالها إلا إلى التطبيقات التي تم توقيعها باستخدام الشهادة نفسها، وليس إلى جميع التطبيقات على الجهاز.

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

دليل نقل البيانات

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

تحقَّق من كيفية استخدام تطبيقك للخدمات. إذا كان تطبيقك يعتمد على خدمات تُشغَّل في الخلفية عندما يكون تطبيقك في وضع السكون، عليك استبدالها. تشمل الحلول المحتملة ما يلي:

  • إذا كان تطبيقك بحاجة إلى إنشاء خدمة تعمل في المقدّمة عندما يكون التطبيق في الخلفية، استخدِم الأسلوب startForegroundService() بدلاً من startService().
  • إذا كانت الخدمة ملحوظة للمستخدم، اجعلها خدمة تعمل في المقدّمة. على سبيل المثال، يجب أن تكون الخدمة التي تشغّل الصوت دائمًا خدمة تعمل في المقدّمة. أنشئ الخدمة باستخدام الأسلوب startForegroundService() بدلاً من startService().
  • ابحث عن طريقة لتكرار وظيفة الخدمة باستخدام مهمة مجدوَلة. إذا كانت الخدمة لا تؤدي مهمة ملحوظة للمستخدم على الفور، من المفترض أن تتمكّن بشكل عام من استخدام مهمة مجدوَلة بدلاً من ذلك.
  • استخدِم Firebase Cloud Messaging لتحديديًا تنشيط تطبيقك عند حدوث أحداث الشبكة، بدلاً من إجراء عمليات طلب في الخلفية.
  • تأجيل العمل في الخلفية إلى أن يظهر التطبيق في المقدّمة بشكل طبيعي

راجِع عناصر البث المحدّدة في بيان تطبيقك. إذا كان البيان يُعلِن عن عنصر استقبال لبث ضمني متأثر، يجب استبداله. تشمل الحلول المحتملة ما يلي:

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