قد تستهلك العمليات في الخلفية استهلاكًا للذاكرة والبطارية. على سبيل المثال، قد يؤدي البث التلقائي إلى بدء العديد من العمليات التي تعمل في الخلفية والتي تم تسجيلها للاستماع إليه، حتى إذا كانت هذه العمليات لا تؤدي الكثير من الأعمال. وقد يكون لذلك تأثير كبير في أداء الجهاز وتجربة المستخدم.
لحلّ هذه المشكلة، يفرض نظام التشغيل Android 7.0 (المستوى 24 لواجهة برمجة التطبيقات) القيود التالية:
- لا تتلقّى التطبيقات التي تستهدف الإصدار 7.0 من نظام التشغيل Android (المستوى 24 من واجهة برمجة التطبيقات) والإصدارات الأحدث
CONNECTIVITY_ACTION
عمليات البث إذا كانت تعلن عن معالج البث في البيان. سيستمر تسجيل التطبيقات لبثّاتCONNECTIVITY_ACTION
إذا سجّلتBroadcastReceiver
معContext.registerReceiver()
وكان هذا السياق لا يزال صالحًا. - لا يمكن للتطبيقات إرسال أو تلقّي عمليات البث عبر
ACTION_NEW_PICTURE
أوACTION_NEW_VIDEO
. ويؤثر هذا التحسين في جميع التطبيقات، وليس فقط تلك التي تستهدف الإصدار 7.0 من Android (المستوى 24 لواجهة برمجة التطبيقات).
إذا كان تطبيقك يستخدم أيًا من هذه النوايا، عليك إزالة التبعيات المرتبطة بها
في أقرب وقت ممكن حتى تتمكّن من استهداف الأجهزة التي تعمل بالإصدار 7.0
من نظام التشغيل Android أو الإصدارات الأحدث بشكل صحيح. يقدّم إطار عمل Android عدة حلول للحدّ من
الحاجة إلى عمليات البث الضمنية هذه. على سبيل المثال، يوفّر JobScheduler
و
WorkManager الجديد آليات فعّالة لجدولة عمليات
الشبكة عند استيفاء شروط محدّدة، مثل الاتصال بشبكة
غير محدودة. يمكنك الآن أيضًا استخدام JobScheduler
للتفاعل مع التغييرات التي تطرأ على موفّري المحتوى. تُحاط عناصر JobInfo
بالمَعلمات التي تستخدمها JobScheduler
لجدولة مهامك. عند استيفاء شروط المهمة، ينفّذ النظام هذه المهمة على JobService
في تطبيقك.
في هذه الصفحة، سنتعرّف على كيفية استخدام طرق بديلة، مثل
JobScheduler
، لتكييف تطبيقك مع هذه
القيود الجديدة.
القيود التي يبدأها المستخدم
في صفحة استخدام البطارية ضمن إعدادات النظام، يمكن للمستخدم الاختيار من بين الخيارات التالية:
- غير محدود: يمكنك السماح بجميع الأعمال التي تعمل في الخلفية، ما قد يؤدي إلى استهلاك المزيد من طاقة البطارية.
- محسَّن (تلقائي): تحسين قدرة التطبيق على تنفيذ المهام في الخلفية، استنادًا إلى كيفية تفاعل المستخدم مع التطبيق
- محظور: يمنع هذا الخيار تشغيل التطبيق في الخلفية تمامًا. قد لا تعمل التطبيقات كما هو متوقع.
إذا كان أحد التطبيقات يعرض بعض السلوكيات السيئة الموضّحة في مؤشرات Android الحيوية، قد يطلب النظام من المستخدم تقييد وصول هذا التطبيق إلى موارد النظام.
إذا لاحظ النظام أنّ أحد التطبيقات يستهلك موارد زائدة، سيتم إرسال إشعار إلى المستخدم ومنحه خيار حظر إجراءات التطبيق. تشمل السلوكيات التي يمكن أن تؤدي إلى ظهور الإشعار ما يلي:
- عمليات قفل التنشيط المفرطة: تم تثبيت قفل تنشيط جزئي واحد لمدة ساعة عندما تكون الشاشة مطفأة.
- الخدمات المفرطة التي تعمل في الخلفية: إذا كان التطبيق يستهدف مستويات واجهة برمجة التطبيقات أقل من 26 وكان لديه خدمات مفرطة تعمل في الخلفية
وتحدِّد الشركة المصنّعة للجهاز القيود الدقيقة المفروضة. على سبيل المثال، في إصدارات AOSP التي تعمل بنظام التشغيل Android 9 (المستوى 28 لواجهة برمجة التطبيقات) أو الإصدارات الأحدث، تسري القيود التالية على التطبيقات التي تعمل في الخلفية وتكون في الحالة "محظورة":
- لا يمكن تشغيل الخدمات التي تعمل في المقدّمة
- إزالة الخدمات الحالية التي تعمل في المقدّمة من المقدّمة
- عدم بدء المنبّهات
- لم يتم تنفيذ المهام
بالإضافة إلى ذلك، إذا كان التطبيق يستهدف الإصدار Android 13 (المستوى 33 لواجهة برمجة التطبيقات) أو الإصدارات الأحدث وكان في
الحالة "محظور"، لن يعرض النظام بث BOOT_COMPLETED
أو
بث LOCKED_BOOT_COMPLETED
إلى أن يبدأ تشغيل التطبيق لأسباب أخرى.
يمكنك الاطّلاع على القيود المحدّدة في القيود المفروضة على إدارة الطاقة.
القيود المفروضة على استقبال عمليات بث أنشطة الشبكة
لا تتلقّى التطبيقات التي تستهدف الإصدار 7.0 من نظام التشغيل Android (المستوى 24 لواجهة برمجة التطبيقات) أحداث CONNECTIVITY_ACTION
إذا كانت
مسجّلة لتلقّيها في البيان، ولن تبدأ العمليات التي تعتمد على
هذه الأحداث. وقد يشكّل ذلك مشكلة للتطبيقات التي تريد
الاستماع إلى تغييرات الشبكة أو تنفيذ أنشطة الشبكة المجمّعة عندما يتم اتصال
الجهاز بشبكة غير محدودة. تتوفّر حاليًا عدة حلول لتجنُّب هذا الحظر في إطار عمل Android، ولكن يعتمد اختيار الحل المناسب على ما تريد أن يحققه تطبيقك.
ملاحظة: يستمر BroadcastReceiver
المسجَّل في
Context.registerReceiver()
في تلقّي هذه البثّات أثناء تشغيل التطبيق.
جدولة مهام الشبكة على اتصالات غير محدودة
عند استخدام فئة JobInfo.Builder
لإنشاء عنصر JobInfo
، طبِّق الطريقة setRequiredNetworkType()
وأرسِل JobInfo.NETWORK_TYPE_UNMETERED
كمَعلمة مهمة. يحدِّد نموذج الرمز البرمجي التالي
موعدًا لتشغيل خدمة عندما يتصل الجهاز بشبكة
غير محدودة أثناء الشحن:
Kotlin
const val MY_BACKGROUND_JOB = 0 ... fun scheduleJob(context: Context) { val jobScheduler = context.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler val job = JobInfo.Builder( MY_BACKGROUND_JOB, ComponentName(context, MyJobService::class.java) ) .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED) .setRequiresCharging(true) .build() jobScheduler.schedule(job) }
Java
public static final int MY_BACKGROUND_JOB = 0; ... public static void scheduleJob(Context context) { JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE); JobInfo job = new JobInfo.Builder( MY_BACKGROUND_JOB, new ComponentName(context, MyJobService.class)) .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED) .setRequiresCharging(true) .build(); js.schedule(job); }
عند استيفاء شروط المهمة، يتلقّى تطبيقك طلب استدعاء لتشغيل onStartJob()
في JobService.class
المحدّد. للاطّلاع على المزيد من الأمثلة على تنفيذ JobScheduler
، يُرجى الاطّلاع على نموذج تطبيق Job Scheduler.
هناك بديل جديد لواجهة JobScheduler، وهو WorkManager، وهي واجهة برمجة تطبيقات تتيح لك جدولة المهام التي تعمل في الخلفية والتي تحتاج إلى اكتمال مضمون، بغض النظر عمّا إذا كانت عملية التطبيق قيد التنفيذ أم لا. يختار WorkManager الطريقة المناسبة لتشغيل العمل (إما مباشرةً على سلسلة مهام في عملية تطبيقك، بالإضافة إلى استخدام JobScheduler أو FirebaseJobDispatcher أو AlarmManager) استنادًا إلى عوامل مثل مستوى واجهة برمجة التطبيقات للجهاز. بالإضافة إلى ذلك، لا يتطلّب WorkManager "خدمات Play" ويقدّم العديد من الميزات المتقدّمة، مثل ربط المهام معًا أو التحقّق من حالة إحدى المهام. لمزيد من المعلومات، يُرجى الاطّلاع على WorkManager.
مراقبة إمكانية الاتصال بالشبكة أثناء تشغيل التطبيق
سيظلّ بإمكان التطبيقات قيد التشغيل الاستماع إلى محتوى "CONNECTIVITY_CHANGE
" باستخدام
BroadcastReceiver
مسجَّل. مع ذلك، توفِّر واجهة برمجة التطبيقات ConnectivityManager
طريقة أكثر فعالية لطلب معاودة الاتصال فقط عند استيفاء شروط الشبكة المحدّدة.
تحدّد عناصر NetworkRequest
مَعلمات NetworkCapabilities
callback للشبكة من حيث NetworkCapabilities
. ويمكنك إنشاء عناصر NetworkRequest
باستخدام الفئة NetworkRequest.Builder
. registerNetworkCallback()
بعد ذلك، تمرّر العنصر NetworkRequest
إلى النظام. عند
استيفاء شروط الشبكة، يتلقّى التطبيق طلب استدعاء لتنفيذ onAvailable()
في فئة ConnectivityManager.NetworkCallback
.
يستمر التطبيق في تلقّي عمليات الاستدعاء إلى أن يخرج من التطبيق أو يتصل بالطريقة
unregisterNetworkCallback()
.
القيود المفروضة على تلقّي عمليات بث الصور والفيديوهات
في نظام Android 7.0 (المستوى 24 من واجهة برمجة التطبيقات)، لا يمكن للتطبيقات إرسال أو تلقّي عمليات البث ACTION_NEW_PICTURE
أو ACTION_NEW_VIDEO
. يساعد هذا التقييد في تخفيف تأثيرات الأداء وتجربة المستخدم عندما يجب أن يتم تفعيل عدة تطبيقات لمعالجة صورة أو فيديو جديدَين. يقدّم نظام التشغيل Android 7.0 (المستوى 24 لواجهة برمجة التطبيقات)JobInfo
وJobParameters
لتوفير حل بديل.
بدء المهام عند تغيير معرّف الموارد المنتظم للمحتوى
لبدء المهام عند تغيير عناوين URL لمحتوى الوسائط، يضيف نظام التشغيل Android 7.0 (المستوى 24 من واجهة برمجة التطبيقات)
واجهة برمجة التطبيقات JobInfo
باستخدام الطرق التالية:
-
JobInfo.TriggerContentUri()
- تُستخدم هذه السمة لتضمين المَعلمات المطلوبة لبدء مهمة عند تغيير معرّف الموارد المنتظم للمحتوى.
-
JobInfo.Builder.addTriggerContentUri()
-
تمرير كائن
TriggerContentUri
إلىJobInfo
. يرصدContentObserver
عنوان URL للمحتوى المُدمَج. إذا كانت هناك عناصرTriggerContentUri
متعددة مرتبطة بمهمة، يقدّم النظام callback حتى إذا أبلغ عن تغيير في معرّف موارد منتظم واحد فقط للمحتوى. -
يمكنك إضافة العلامة
TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS
لتشغيل المهمة في حال تغيير أي عناصر تابعة لمعرّف الموارد المنتظم (URI) المحدّد. تتطابق هذه العلامة مع المَعلمةnotifyForDescendants
التي تم تمريرها إلىregisterContentObserver()
.
ملاحظة: لا يمكن استخدام TriggerContentUri()
مع setPeriodic()
أو setPersisted()
. للتتبّع المستمر لتغييرات المحتوى، يمكنك جدولة
JobInfo
جديدة قبل أن ينتهي JobService
التطبيق من معالجة آخر مكالمة واردة.
يحدّد الرمز النموذجي التالي مهمة لبدء تشغيلها عندما يُبلغ النظام عن
تغيير في معرّف الموارد المنتظم (URI) للمحتوى، MEDIA_URI
:
Kotlin
const val MY_BACKGROUND_JOB = 0 ... fun scheduleJob(context: Context) { val jobScheduler = context.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler val job = JobInfo.Builder( MY_BACKGROUND_JOB, ComponentName(context, MediaContentJob::class.java) ) .addTriggerContentUri( JobInfo.TriggerContentUri( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS ) ) .build() jobScheduler.schedule(job) }
Java
public static final int MY_BACKGROUND_JOB = 0; ... public static void scheduleJob(Context context) { JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE); JobInfo.Builder builder = new JobInfo.Builder( MY_BACKGROUND_JOB, new ComponentName(context, MediaContentJob.class)); builder.addTriggerContentUri( new JobInfo.TriggerContentUri(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS)); js.schedule(builder.build()); }
عندما يُبلغ النظام عن تغيير في عناوين URL المحدّدة للمحتوى، يتلقّى تطبيقك
طلب استدعاء ويتم
تمرير عنصر JobParameters
إلى طريقة onStartJob()
في MediaContentJob.class
.
تحديد هيئات المحتوى التي تسبّبت في بدء إحدى المهام
يضيف نظام التشغيل Android 7.0 (المستوى 24 من واجهة برمجة التطبيقات) أيضًا JobParameters
لسماح تطبيقك بتلقّي معلومات مفيدة عن جهات التحكّم في المحتوى
وعناوين URL التي أدّت إلى بدء المهمة:
-
Uri[] getTriggeredContentUris()
-
لعرض صفيف من عناوين URL التي أدّت إلى بدء المهمة. سيكون هذا الرمز
null
إذا لم يتم بدء المهمة من خلال أي عناوين URL (على سبيل المثال، تم بدء المهمة بسبب الموعد النهائي أو لأي سبب آخر)، أو إذا كان عدد عناوين URL التي تم تغييرها أكبر من 50. -
String[] getTriggeredContentAuthorities()
-
تعرض صفيفًا من سلاسل جهات إصدار المحتوى التي تسبّبت في بدء المهمة.
إذا لم تكن الصفيف المعروضة هي
null
، استخدِمgetTriggeredContentUris()
لاسترداد تفاصيل عناوين URL التي تغيّرت.
يلغي الرمز النموذجي التالي الطريقة JobService.onStartJob()
ويسجّل مراجع المحتوى ومعرّفات الموارد المنتظمة (URI) التي أدّت إلى تفعيل المهمة:
Kotlin
override fun onStartJob(params: JobParameters): Boolean { StringBuilder().apply { append("Media content has changed:\n") params.triggeredContentAuthorities?.also { authorities -> append("Authorities: ${authorities.joinToString(", ")}\n") append(params.triggeredContentUris?.joinToString("\n")) } ?: append("(No content)") Log.i(TAG, toString()) } return true }
Java
@Override public boolean onStartJob(JobParameters params) { StringBuilder sb = new StringBuilder(); sb.append("Media content has changed:\n"); if (params.getTriggeredContentAuthorities() != null) { sb.append("Authorities: "); boolean first = true; for (String auth : params.getTriggeredContentAuthorities()) { if (first) { first = false; } else { sb.append(", "); } sb.append(auth); } if (params.getTriggeredContentUris() != null) { for (Uri uri : params.getTriggeredContentUris()) { sb.append("\n"); sb.append(uri); } } } else { sb.append("(No content)"); } Log.i(TAG, sb.toString()); return true; }
تحسين تطبيقك بشكل أكبر
يمكن أن يؤدي تحسين تطبيقاتك لتعمل على الأجهزة ذات الذاكرة المنخفضة أو في الحالات التي تكون فيها الذاكرة منخفضة إلى تحسين الأداء وتجربة المستخدم. يمكن أن تساعد إزالة التبعيات على الخدمات التي تعمل في الخلفية ومقبِّلات البث الضمنية المسجَّلة في البيان في تشغيل تطبيقك بشكل أفضل على هذه الأجهزة. على الرغم من أنّه يتّخذ نظام التشغيل Android 7.0 (المستوى 24 لواجهة برمجة التطبيقات) خطوات للحدّ من بعض هذه المشاكل، فإنه يُنصح بتحسين تطبيقك لتشغيله بدون استخدام هذه العمليات التي تعمل في الخلفية بالكامل.
يمكن أن تساعدك أوامر Android Debug Bridge (ADB) التالية في اختبار سلوك التطبيق مع إيقاف العمليات التي تعمل في الخلفية:
- لمحاكاة الظروف التي لا تتوفّر فيها عمليات البث الضمني والخدمات التي تعمل في الخلفية، أدخِل الأمر التالي:
-
$ adb shell cmd appops set <package_name> RUN_IN_BACKGROUND ignore
- لإعادة تفعيل عمليات البث الضمني والخدمات التي تعمل في الخلفية، أدخِل الأمر التالي:
-
$ adb shell cmd appops set <package_name> RUN_IN_BACKGROUND allow
- يمكنك محاكاة وضع المستخدم لتطبيقك في الحالة "محظور" لاستهلاكه للبطارية في الخلفية. يمنع هذا الإعداد تطبيقك من التشغيل في الخلفية. لإجراء ذلك، نفِّذ الأمر التالي في نافذة وحدة طرفية:
-
$ adb shell cmd appops set <PACKAGE_NAME> RUN_ANY_IN_BACKGROUND deny