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

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

لحلّ هذه المشكلة، يفرض نظام التشغيل 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 تضم الكائنات المعاملين التي 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، يُرجى الاطّلاع على نموذج تطبيق Job Scheduler.

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

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

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

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

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

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

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

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

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

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

تحديد مصادر المحتوى التي أدّت إلى أداء وظيفة

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

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