قيود النظام على المهام التي يتم تنفيذها في الخلفية

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

لتجنُّب قيود النظام، تأكَّد من استخدام واجهة برمجة التطبيقات المناسبة لمهمتك التي يتم تشغيلها في الخلفية. تساعدك مستندات نظرة عامة على المهام التي يتم تشغيلها في الخلفية في اختيار واجهة برمجة التطبيقات المناسبة لاحتياجاتك.

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

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

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

  1. عمليات قفل التنشيط الزائدة عن الحد: عملية قفل تنشيط جزئي واحدة يتم الاحتفاظ بها لمدة ساعة عندما تكون الشاشة مطفأة
  2. الخدمات الزائدة عن الحد التي يتم تشغيلها في الخلفية: إذا كان التطبيق يستهدف مستويات واجهة برمجة التطبيقات الأقل من 26 وكان يتضمّن خدمات زائدة عن الحد يتم تشغيلها في الخلفية

تحدّد الشركة المصنّعة للجهاز القيود الدقيقة المفروضة. على سبيل المثال، في إصدارات AOSP، لا يمكن للتطبيقات المحظورة تشغيل المهام أو تفعيل المنبّهات أو استخدام الشبكة، إلا عندما يكون التطبيق في المقدّمة.

القيود المفروضة على تلقّي عمليات بث نشاط الشبكة

لا تتلقّى التطبيقات عمليات بث CONNECTIVITY_ACTION إذا تم تسجيلها لتلقّيها في بيانها، ولن تبدأ العمليات التي تعتمد على عملية البث هذه. قد يطرح ذلك مشكلة للتطبيقات التي تريد تتبّع تغييرات الشبكة أو تنفيذ أنشطة الشبكة المجمّعة عندما يتصل الجهاز بشبكة لا تفرض تكلفة استخدام. تتوفّر عدة حلول لتجنُّب هذا القيد في إطار عمل Android، ولكن يعتمد اختيار الحل المناسب على ما تريد أن يحققه تطبيقك.

جدولة العمل على الاتصالات غير المحدودة

عند إنشاء WorkRequest، أضِف Constraint من النوع NetworkType.UNMETERED.

fun scheduleWork(context: Context) {
    val workManager = WorkManager.getInstance(context)
    val workRequest = OneTimeWorkRequestBuilder<MyWorker>()
       .setConstraints(
           Constraints.Builder()
               .setRequiredNetworkType(NetworkType.UNMETERED)
               .build()
           )
       .build()

    workManager.enqueue(workRequest)
}

عند استيفاء شروط عملك، يتلقّى تطبيقك ردّ اتصال لتشغيل الـ doWork() طريقة في فئة Worker المحدّدة.

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

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

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

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

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

لا يمكن للتطبيقات إرسال عمليات بث ACTION_NEW_PICTURE أو ACTION_NEW_VIDEO أو تلقّيها. يساعد هذا القيد في تخفيف التأثيرات في الأداء وتجربة المستخدم عندما يجب تنبيه عدة تطبيقات لمعالجة صورة أو فيديو جديدَين.

تحديد مراجع المحتوى التي بدأت العمل

WorkerParameters تسمح لتطبيقك بتلقّي معلومات مفيدة حول ما هي مراجع المحتوى وعناوين URI التي بدأت العمل:

List<Uri> getTriggeredContentUris()

تعرض قائمة بعناوين URI التي بدأت العمل. تكون هذه القائمة فارغة إذا لم تبدأ أي عناوين URI العمل (على سبيل المثال، إذا بدأ العمل بسبب موعد نهائي أو سبب آخر)، أو إذا كان عدد عناوين URI التي تم تغييرها أكبر من 50.

List<String> getTriggeredContentAuthorities()

تعرض قائمة سلاسل لمراجع المحتوى التي بدأت العمل. إذا لم تكن القائمة المعروضة فارغة، استخدِم getTriggeredContentUris() لاسترداد تفاصيل عناوين URI التي تم تغييرها.

يستبدل رمز نموذجي طريقة CoroutineWorker.doWork() ويسجِّل مراجع المحتوى ومعرّفات الموارد المنتظمة (URI) التي بدأت المهمة:

class MyWorker(
    appContext: Context,
    params: WorkerParameters
): CoroutineWorker(appContext, params)
    override suspend fun doWork(): Result {
        StringBuilder().apply {
            append("Media content has changed:\n")
            params.triggeredContentAuthorities
                .takeIf { it.isNotEmpty() }
                ?.let { authorities ->
                    append("Authorities: ${authorities.joinToString(", ")}\n")
                    append(params.triggeredContentUris.joinToString("\n"))
                } ?: append("(No content)")
            Log.i(TAG, toString())
        }
        return Result.success()
    }
}

اختبار التطبيق في ظل قيود النظام

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

يمكن أن تساعدك بعض أوامر 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

تحسين تطبيقك بشكلٍ أكبر

للحصول على طرق جيدة أخرى لتحسين سلوك المهام التي يتم تشغيلها في الخلفية، اطّلِع على مستندات تحسين استخدام البطارية لواجهات برمجة التطبيقات الخاصة بجدولة المهام.