يمكن أن تستهلك العمليات التي تعمل في الخلفية ذاكرة وبطارية بشكل كبير. على سبيل المثال، قد يؤدي البث التلقائي إلى بدء العديد من العمليات التي تعمل في الخلفية والتي تم تسجيلها للانتباه إليه، حتى إذا لم تكن هذه العمليات تؤدي الكثير من العمل. ويمكن أن يكون لهذا الإجراء أثر عميق في كل من أداء الجهاز وتجربة المستخدم.
لحلّ هذه المشكلة، يفرض نظام التشغيل 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 لواجهة برمجة التطبيقات) أو الإصدارات الأحدث، تسري القيود التالية على التطبيقات التي تعمل في الخلفية وتكون في الحالة "محظورة":
- لا يمكن تشغيل الخدمات التي تعمل في المقدّمة
- إزالة الخدمات الحالية التي تعمل في المقدّمة من المقدّمة
- عدم بدء المنبّهات
- عدم تنفيذ المهام
بالإضافة إلى ذلك، إذا كان التطبيق يستهدف الإصدار 13 من نظام التشغيل Android (المستوى 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
، اطّلِع على نموذج تطبيق JobScheduler.
هناك بديل جديد لواجهة 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()
- تُستخدم هذه السمة لتضمين المَعلمات المطلوبة لبدء مهمة عند تغيير معرّف الموارد المنتظم (URI) للمحتوى.
-
JobInfo.Builder.addTriggerContentUri()
-
تمرير عنصر
TriggerContentUri
إلىJobInfo
يرصدContentObserver
عنوان URL للمحتوى المُدمَج. إذا كانت هناك عناصرTriggerContentUri
متعددة مرتبطة بوظيفة، يقدّم النظام callback حتى إذا أبلغ عن تغيير في معرّف موارد منتظم واحد فقط للمحتوى. -
أضِف العلامة
TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS
لبدء المهمة في حال تغيُّر أيّ من العناصر الفرعية لمعرّف الموارد المنتظم المحدّد. تتطابق هذه العلامة مع المَعلمةnotifyForDescendants
التي تم تمريرها إلىregisterContentObserver()
.
ملاحظة: لا يمكن استخدام TriggerContentUri()
مع setPeriodic()
أو setPersisted()
. للتتبّع المستمر لتغييرات المحتوى، يمكنك جدولة
JobInfo
جديدة قبل أن ينتهي JobService
التطبيق من معالجة آخر مكالمة واردة.
يحدِّد نموذج الرمز البرمجي التالي موعدًا لبدء مهمة عندما يُبلغ النظام عن
تغيير في معرّف الموارد المنتظم للمحتوى، 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