تحسين الخلفية

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

للتخفيف من حدة هذه المشكلة، يطبّق نظام التشغيل 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 من واجهة برمجة التطبيقات).

إذا كان تطبيقك يستخدم أيًا من هذه الأهداف، عليك إزالة التبعيات عليها في أقرب وقت ممكن حتى تتمكّن من استهداف الأجهزة التي تعمل بنظام التشغيل Android 7.0 أو الإصدارات الأحدث بشكل صحيح. يوفّر إطار عمل Android عدة حلول للحدّ من الحاجة إلى عمليات البث الضمني هذه. على سبيل المثال، توفّر واجهة برمجة التطبيقات JobScheduler وواجهة برمجة التطبيقات الجديدة WorkManager آليات فعّالة لجدولة عمليات الشبكة عند استيفاء شروط محدّدة، مثل الاتصال بشبكة لا تفرض تكلفة استخدام. يمكنك الآن أيضًا استخدام JobScheduler للتفاعل مع التغييرات التي تطرأ على أدوات توفير المحتوى. JobInfo objects encapsulate the parameters that JobScheduler uses to schedule your job. عند استيفاء شروط المهمة، ينفّذ النظام هذه المهمة على JobService في تطبيقك.

في هذه الصفحة، سنتعرّف على كيفية استخدام طرق بديلة، مثل JobScheduler، لتكييف تطبيقك مع هذه القيود الجديدة.

القيود التي يفرضها المستخدم

في صفحة استخدام البطارية ضمن إعدادات النظام، يمكن للمستخدم الاختيار من بين الخيارات التالية:

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

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

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

  • عمليات قفل التنشيط الزائدة عن الحد: قفل تنشيط جزئي واحد لمدة ساعة عندما تكون الشاشة مطفأة
  • الخدمات التي تُشغَّل في الخلفية بشكل مفرط: إذا كان التطبيق يستهدف مستويات واجهة برمجة التطبيقات الأقل من 26 ويحتوي على خدمات تُشغَّل في الخلفية بشكل مفرط

وتحدّد الشركة المصنّعة للجهاز القيود الدقيقة المفروضة. على سبيل المثال، في إصدارات مشروع Android المفتوح المصدر (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.

‫WorkManager هو بديل جديد لـ JobScheduler، وهو واجهة برمجة تطبيقات تتيح لك جدولة المهام في الخلفية التي تحتاج إلى ضمان إكمالها، بغض النظر عمّا إذا كانت عملية التطبيق متاحة أم لا. يختار WorkManager الطريقة المناسبة لتنفيذ العمل (إما مباشرةً في سلسلة محادثات في عملية تطبيقك أو باستخدام JobScheduler أو FirebaseJobDispatcher أو AlarmManager) استنادًا إلى عوامل مثل مستوى واجهة برمجة التطبيقات للجهاز. بالإضافة إلى ذلك، لا يتطلّب WorkManager استخدام "خدمات Play"، ويوفّر العديد من الميزات المتقدّمة، مثل ربط المهام معًا أو التحقّق من حالة مهمة معيّنة. لمزيد من المعلومات، يُرجى الاطّلاع على WorkManager.

مراقبة الاتصال بالشبكة أثناء تشغيل التطبيق

لا يزال بإمكان التطبيقات قيد التشغيل الاستماع إلى CONNECTIVITY_CHANGE باستخدام BroadcastReceiver مسجَّل. ومع ذلك، توفّر واجهة برمجة التطبيقات ConnectivityManager طريقة أكثر فعالية لطلب دالة رد نداء فقط عند استيفاء شروط معيّنة للشبكة.

تحدّد عناصر NetworkRequest مَعلمات ردّ الاتصال بالشبكة من حيث NetworkCapabilities. يمكنك إنشاء كائنات NetworkRequest باستخدام الفئة NetworkRequest.Builder. registerNetworkCallback() ثم يمرر الكائن NetworkRequest إلى النظام. عند استيفاء شروط الشبكة، يتلقّى التطبيق طلب معاودة الاتصال لتنفيذ طريقة onAvailable() المحدّدة في فئة ConnectivityManager.NetworkCallback.

يستمر التطبيق في تلقّي عمليات الاستدعاء إلى أن يخرج التطبيق أو يستدعي unregisterNetworkCallback().

القيود المفروضة على تلقّي عمليات بث الصور والفيديوهات

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

بدء المهام عند تغيير معرّف الموارد المنتظم للمحتوى

لتشغيل المهام عند تغيير معرّف الموارد المنتظم للمحتوى، يوسّع Android 7.0 (المستوى 24 من واجهة برمجة التطبيقات) واجهة برمجة التطبيقات JobInfo باستخدام الطرق التالية:

JobInfo.TriggerContentUri()
تتضمّن هذه الفئة المَعلمات المطلوبة لتشغيل مهمة عند تغيير معرّف الموارد المنتظم للمحتوى.
JobInfo.Builder.addTriggerContentUri()
يتم تمرير عنصر TriggerContentUri إلى JobInfo. يُراقب ContentObserver معرّف الموارد المنتظم للمحتوى المغلَّف. إذا كانت هناك عدة عناصر TriggerContentUri مرتبطة بمهمة، يوفّر النظام عملية استدعاء حتى إذا أبلغ عن تغيير في معرّف موارد منتظم واحد فقط للمحتوى.
أضِف العلامة TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS لتشغيل المهمة في حال تغيير أي من العناصر التابعة لمعرّف الموارد المنتظم (URI) المحدّد. تتوافق هذه العلامة مع مَعلمة 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());
}

عندما يُبلغ النظام عن تغيير في معرّفات الموارد المنتظمة للمحتوى المحدّدة، يتلقّى تطبيقك طلب استدعاء ويتم تمرير عنصر JobParameters إلى طريقة onStartJob() في MediaContentJob.class.

تحديد مصادر المحتوى الموثوقة التي أدّت إلى إنشاء مهمة

يوفّر Android 7.0 (مستوى واجهة برمجة التطبيقات 24) أيضًا JobParameters للسماح لتطبيقك بتلقّي معلومات مفيدة حول مراجع المحتوى ومعرّفات الموارد المنتظمة (URI) التي أدّت إلى تشغيل المهمة:

Uri[] getTriggeredContentUris()
تعرض هذه السمة مصفوفة من عناوين URI التي أدّت إلى بدء المهمة. ستكون هذه القيمة null إذا لم يتم تشغيل المهمة من خلال أي معرّفات موارد منتظمة (على سبيل المثال، تم تشغيل المهمة بسبب موعد نهائي أو سبب آخر)، أو إذا كان عدد معرّفات الموارد المنتظمة التي تم تغييرها أكبر من 50.
String[] getTriggeredContentAuthorities()
تعرض مصفوفة سلاسل نصية لمصادر المحتوى التي فعّلت المهمة. إذا لم تكن المصفوفة المعروضة null، استخدِم getTriggeredContentUris() لاسترداد تفاصيل معرّفات الموارد المنتظمة (URI) التي تم تغييرها.

يتجاوز الرمز النموذجي التالي طريقة 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