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

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

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

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

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

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

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

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

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

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

جدولة العمل على الاتصالات التي لا تفرض تكلفة استخدام

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

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 طريقة أكثر فعالية لطلب معاودة الاتصال فقط عند استيفاء شروط معيّنة للشبكة.

تحدّد عناصر 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,
    private val 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

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

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