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

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

نظرة عامة

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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