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

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

لحلّ هذه المشكلة، يفرض نظام التشغيل 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