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

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

نظرة عامة

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

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

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

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

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

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

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

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

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

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

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

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

في كثير من الحالات، يمكن لتطبيقك استبدال الخدمات التي تعمل في الخلفية بوظائف JobScheduler. على سبيل المثال، يحتاج Cool PhotoApp إلى التحقق مما إذا كان المستخدم قد تلقى الصور المشتركة من الأصدقاء، حتى إذا كان التطبيق لا يعمل في المقدمة. في السابق، كان التطبيق يستخدم خدمة في الخلفية كانت تتحقق من التخزين السحابي للتطبيق. للانتقال إلى 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 من واجهة برمجة التطبيقات) قيودًا على عمليات البث، كما هو موضّح في تحسين الخلفية. ويجعل 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()، بدلاً من الإعلان عن المُستلِم في ملف البيان.
  • استخدم مهمة مجدولة للتحقق من الشرط الذي كان سيؤدي إلى البث الضمني.