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

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

للحدّ من هذه المشكلة، يطبّق Android 7.0 (المستوى 24 من واجهة برمجة التطبيقات) القيود التالية:

  • لا تتلقّى التطبيقات التي تستهدف الإصدار 7.0 من نظام التشغيل Android (المستوى 24 من واجهة برمجة التطبيقات) والإصدارات الأحدث عمليات بث من نوع CONNECTIVITY_ACTION إذا تم الإعلان عن متلقّي البث في البيان. وستظل التطبيقات تتلقّى عمليات بث CONNECTIVITY_ACTION إذا كانت تسجّل BroadcastReceiver باستخدام Context.registerReceiver() ولا يزال هذا السياق صالحًا.
  • لا يمكن للتطبيقات إرسال أو تلقّي رسائل بث ACTION_NEW_PICTURE أو ACTION_NEW_VIDEO. يؤثّر هذا التحسين على جميع التطبيقات، وليس فقط على التطبيقات التي تستهدف الإصدار Android 7.0 (المستوى 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، يمكنك الاطّلاع على نموذج تطبيق JobScheduler.

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

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

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

تحدّد كائنات NetworkRequest معلَمات معاودة الاتصال بالشبكة من حيث 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 لتوفير حل بديل.

تشغيل المهام عند تغيير معرّف الموارد المنتظم (URI) للمحتوى

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

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

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

تحديد هيئات المحتوى التي أدت إلى ظهور وظيفة

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

Uri[] getTriggeredContentUris()
تعرض مصفوفة من معرّفات الموارد المنتظمة (URI) التي أدّت إلى تنفيذ المهمة. سيكون هذا الحقل بالقيمة null إذا لم يتم تشغيل المهمة من خلال معرّفات الموارد المنتظمة (URI) (على سبيل المثال، إذا تم بدء المهمة بسبب موعد نهائي أو لسبب آخر)، أو إذا كان عدد معرّفات الموارد المنتظمة (URI) التي تم تغييرها أكبر من 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;
}

تحسين تطبيقك أكثر

يمكن أن يؤدي تحسين أداء التطبيقات لتشغيلها على الأجهزة ذات الذاكرة المنخفضة أو في ظروف انخفاض الذاكرة إلى تحسين الأداء وتجربة المستخدم. إنّ إزالة العناصر الاعتمادية على الخدمات في الخلفية وأجهزة استقبال البث الضمني المسجّلة في البيان يمكن أن تساعد في تشغيل تطبيقك بشكل أفضل على هذه الأجهزة. ومع أنّ الإصدار 7.0 من نظام التشغيل Android (المستوى 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