أخطاء ANR

عند حظر سلسلة محادثات واجهة المستخدم لتطبيق Android لفترة طويلة جدًا، يتم تشغيل خطأ "التطبيق لا يستجيب" (ANR). إذا كان التطبيق في المقدمة، يعرض النظام مربع حوار للمستخدم، كما هو موضح في الشكل 1. يمنح مربع حوار ANR للمستخدم الفرصة لفرض إنهاء التطبيق.

الشكل 1. مربّع حوار ANR المعروض للمستخدم

الشكل 1. مربّع حوار ANR المعروض للمستخدم

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

يتم تشغيل خطأ ANR لتطبيقك عند حدوث أحد الحالات التالية:

  • انتهاء مهلة إرسال الإدخال: إذا لم يستجِب تطبيقك لحدث إدخال في غضون 5 ثوانٍ (مثل الضغط على المفتاح أو لمس الشاشة)
  • تنفيذ الخدمة: إذا لم تتمكّن خدمة أعلن عنها تطبيقك من إكمال تنفيذ Service.onCreate() وService.onStartCommand()/Service.onBind() خلال بضع ثوانٍ.
  • Service.startForeground() : إذا كان تطبيقك يستخدم Context.startForegroundService() لبدء خدمة جديدة في المقدّمة، ولكن الخدمة لا تستدعي startForeground() في غضون 5 ثوانٍ.
  • إعلان النية بالشراء: إذا لم يتم تنفيذ BroadcastReceiver خلال فترة زمنية محددة. إذا كان التطبيق يحتوي على أي نشاط في المقدمة، تكون هذه المهلة 5 ثوانٍ.
  • تفاعلات JobScheduler: في حال عدم عودة JobService من JobService.onStartJob() أو JobService.onStopJob() في غضون بضع ثوان، أو في حال بدء مهمة نفّذها المستخدم وعدم استدعاء تطبيقك JobService.setNotification() في غضون بضع ثوان من طلب JobService.onStartJob(). بالنسبة إلى التطبيقات التي تستهدف الإصدار 13 من نظام التشغيل Android والإصدارات الأقدم، تكون أخطاء ANR صامتة ولا يتم إبلاغ التطبيق بها. بالنسبة إلى التطبيقات التي تستهدف الإصدار 14 من نظام التشغيل Android والإصدارات الأحدث، تظهر أخطاء ANR بشكل صريح ويتم إبلاغ التطبيق بها.

إذا كان تطبيقك يواجه أخطاء ANR، يمكنك اتّباع الإرشادات الواردة في هذه المقالة لتشخيص المشكلة وحلّها.

اكتشاف المشكلة

إذا سبق لك نشر تطبيقك، يمكنك استخدام "مؤشرات Android الحيوية" للاطّلاع على معلومات عن أخطاء ANR لتطبيقك. ويمكنك استخدام أدوات أخرى لرصد أخطاء ANR على أرض الواقع، ولكن تجدر الإشارة إلى أنّ الأدوات التابعة لجهات خارجية لا يمكنها الإبلاغ عن أخطاء ANR على الإصدارات القديمة من Android (الإصدار 10 والإصدارات الأقدم)، على عكس "مؤشرات Android الحيوية".

مؤشرات Android الحيوية

يمكن أن تساعدك "مؤشرات Android الحيوية" في مراقبة نسبة أخطاء ANR لتطبيقك وتحسينها. تقيس "مؤشرات Android الحيوية" العديد من نِسب أخطاء ANR:

  • نسبة أحداث ANR: النسبة المئوية للمستخدمين النشطين يوميًا الذين واجهوا أي نوع من أخطاء ANR.
  • نسبة أخطاء ANR التي لاحظها المستخدمون: تعرض هذه النسبة النسبة المئوية للمستخدمين النشطين يوميًا الذين واجهوا خطأ ANR واحدًا على الأقل من الأخطاء الملحوظة. في الوقت الحالي، لا يلاحظ المستخدمون سوى أخطاء ANR من النوع Input dispatching timed out.
  • نسبة أخطاء ANR المتعددة: هي النسبة المئوية للمستخدمين النشطين يوميًا الذين واجهوا خطأيّ ANR على الأقل.

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

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

حدّد Play اثنين من الحدّ الأدنى للسلوك السيئ في هذا المقياس:

  • الحد الأدنى العام لخلل الأداء: يواجه ما لا يقل عن 0.47% من المستخدمين النشطين يوميًا خطأ ANR ملحوظًا في جميع طُرز الأجهزة.
  • الحد الأدنى لخلل الأداء لكل جهاز: يواجه 8% على الأقل من المستخدمين يوميًا خطأ ANR لاحظه المستخدمون، لطراز واحد من الأجهزة.

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

يمكن لـ "مؤشرات Android الحيوية" تنبيهك عبر Play Console عندما يعرض تطبيقك أخطاء ANR بشكلٍ مفرط.

لمزيد من المعلومات حول الطريقة التي يجمع بها Google Play بيانات "مؤشرات Android الحيوية"، يُرجى الاطّلاع على مستندات Play Console.

تشخيص أخطاء ANR

هناك بعض الأنماط الشائعة التي يجب البحث عنها عند تشخيص أخطاء ANR:

  • يُجري التطبيق عمليات بطيئة تتضمّن وحدات الإدخال والإخراج في سلسلة التعليمات الرئيسية.
  • يُجري التطبيق عملية حسابية طويلة على سلسلة التعليمات الرئيسية.
  • تقوم سلسلة التعليمات الرئيسية باستدعاء صنف Binder متزامن لعملية أخرى، وتستغرق إعادة هذه العملية الأخرى وقتًا طويلاً.
  • تم حظر سلسلة التعليمات الرئيسية في انتظار كتلة متزامنة لعملية طويلة تحدث في سلسلة تعليمات أخرى.
  • إن سلسلة التعليمات الرئيسية في حالة توقف تام مع سلسلة تعليمات أخرى، إما في المعالجة التي تجريها أو عبر طلب الصنف Binder. لا تنتظر سلسلة التعليمات الرئيسية فقط عملية طويلة للانتهاء، ولكنها في حالة توقف تام. لمزيد من المعلومات، راجِع الإغلاق اللامع على ويكيبيديا.

ويمكن أن تساعدك الأساليب التالية في تحديد سبب أخطاء ANR.

إحصاءات صحية

توفّر HealthStats مقاييس حول سلامة التطبيق من خلال تسجيل إجمالي الوقت للمستخدم والنظام ووقت وحدة المعالجة المركزية (CPU) والشبكة وإحصاءات الراديو ووقت تشغيل/إيقاف الشاشة وإنذارات الاستيقاظ. وقد يساعد ذلك في قياس الاستخدام العام لوحدة المعالجة المركزية (CPU) وتصريف البطارية.

تصحيح الأخطاء

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

معلومات خروج التطبيق

يتوفّر ApplicationExitInfo على نظام التشغيل Android 11 (المستوى 30 لواجهة برمجة التطبيقات) أو الإصدارات الأحدث، ويقدّم معلومات حول سبب الخروج من التطبيق. ويشمل ذلك أخطاء ANR أو انخفاض الذاكرة أو أعطال التطبيق أو الاستخدام المفرط لوحدة المعالجة المركزية (CPU) أو انقطاع الخدمة لدى المستخدم أو انقطاعات النظام أو التغييرات في أذونات التشغيل.

الوضع المتشدد

إنّ استخدام أداة StrictMode يساعدك في العثور على العمليات غير المقصودة المتعلقة بوحدات الإدخال والإخراج في سلسلة التعليمات الرئيسية أثناء تطوير تطبيقك. يمكنك استخدام StrictMode على مستوى التطبيق أو النشاط.

تفعيل مربعات حوار ANR في الخلفية

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

عرض التتبُّع

يمكنك استخدام Traceview للحصول على تتبُّع لتطبيقك قيد التشغيل أثناء الاطّلاع على حالات الاستخدام وتحديد الأماكن التي تكون فيها سلسلة التعليمات الرئيسية مشغولةً. للحصول على معلومات حول كيفية استخدام Traceview، يُرجى الاطّلاع على التوصيف باستخدام Traceview وdmtracedump.

سحب ملف تتبُّع

يخزِّن Android معلومات التتبُّع عندما يواجه خطأ ANR. في الإصدارات القديمة من نظام التشغيل، يوجد ملف /data/anr/traces.txt واحد على الجهاز. في الإصدارات الأحدث من نظام التشغيل، هناك عدة ملفات /data/anr/anr_*. يمكنك الوصول إلى آثار أخطاء ANR من جهاز أو محاكٍ باستخدام Android Debug Bridge (adb) باعتباره جذرًا:

adb root
adb shell ls /data/anr
adb pull /data/anr/<filename>

يمكنك تسجيل تقرير خطأ من جهاز مادي باستخدام خيار "إعداد تقرير بالأخطاء" على الجهاز أو باستخدام أمر adb errorreport على جهاز التطوير. وللحصول على مزيد من المعلومات، يمكنك الاطّلاع على التقاط تقارير الأخطاء وقراءتها.

حلّ المشاكل

بعد تحديد المشكلة، يمكنك استخدام النصائح الواردة في هذا القسم لحل المشاكل الشائعة.

رمز بطيء في سلسلة التعليمات الرئيسية

حدد الأماكن في التعليمة البرمجية التي تكون فيها سلسلة التعليمات الرئيسية للتطبيق مشغولة لأكثر من 5 ثوانٍ. ابحث عن حالات الاستخدام المريبة في تطبيقك وحاول إعادة إنتاج خطأ ANR.

على سبيل المثال، يوضّح الشكل 2 مخططًا زمنيًا لـ Traceview يكون فيها سلسلة التعليمات الرئيسية مشغولاً لأكثر من 5 ثوانٍ.

الشكل 2. المخطط الزمني للتتبع يعرض
مؤشرًا رئيسيًا مزدحمًا

الشكل 2. المخطط الزمني في Traceview يعرض سلسلة محادثات رئيسية مزدحمة

يوضِّح الشكل 2 أنّ معظم الرمز المسيء يحدث في معالج onClick(View)، كما هو موضّح في مثال الرمز التالي:

Kotlin

override fun onClick(v: View) {
    // This task runs on the main thread.
    BubbleSort.sort(data)
}

Java

@Override
public void onClick(View view) {
    // This task runs on the main thread.
    BubbleSort.sort(data);
}

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

IO في سلسلة التعليمات الرئيسية

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

من أمثلة عمليات IO: عمليات الشبكة والتخزين. لمزيد من المعلومات، راجع إجراء عمليات الشبكة وحفظ البيانات.

تزايد الطلب على دالة الاستبعاد المتبادل

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

على سبيل المثال، يوضح الشكل 4 مخططًا زمنيًا لـ Traceview يتم فيه تنفيذ معظم الأعمال على سلسلة تعليمات عامل تشغيل.

الشكل 4. المخطط الزمني في Traceview الذي يعرض العمل الذي يتم تنفيذه على سلسلة تعليمات
عاملة

الشكل 4. المخطط الزمني في Traceview الذي يعرض العمل الذي يتم تنفيذه على سلسلة تعليمات عاملة

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

أما إذا لم تتمكّن سلسلة التعليمات الرئيسية من استئناف تنفيذها، فستكون في حالة BLOCKED ولا يمكنها الاستجابة إلى الأحداث. تظهر الحالة على شاشة جهاز Android باسم مراقب أو انتظار، كما هو موضّح في الشكل 5.

الشكل 5. مؤشر الترابط الرئيسي في
حالة المراقبة

الشكل 5. سلسلة التعليمات الرئيسية في حالة المراقبة

يعرض تقرير التتبُّع التالي سلسلة التعليمات الرئيسية لتطبيق تم حظرها في انتظار توفُّر مورد:

...
AsyncTask #2" prio=5 tid=18 Runnable
  | group="main" sCount=0 dsCount=0 obj=0x12c333a0 self=0x94c87100
  | sysTid=25287 nice=10 cgrp=default sched=0/0 handle=0x94b80920
  | state=R schedstat=( 0 0 0 ) utm=757 stm=0 core=3 HZ=100
  | stack=0x94a7e000-0x94a80000 stackSize=1038KB
  | held mutexes= "mutator lock"(shared held)
  at com.android.developer.anrsample.BubbleSort.sort(BubbleSort.java:8)
  at com.android.developer.anrsample.MainActivity$LockTask.doInBackground(MainActivity.java:147)
  - locked <0x083105ee> (a java.lang.Boolean)
  at com.android.developer.anrsample.MainActivity$LockTask.doInBackground(MainActivity.java:135)
  at android.os.AsyncTask$2.call(AsyncTask.java:305)
  at java.util.concurrent.FutureTask.run(FutureTask.java:237)
  at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:243)
  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
  at java.lang.Thread.run(Thread.java:761)
...

يمكن أن تساعدك مراجعة عملية التتبُّع في تحديد مكان الرمز الذي يحظر سلسلة التعليمات الرئيسية. ويكون الرمز التالي مسؤولاً عن الاحتفاظ بالقفل الذي يمنع سلسلة التعليمات الرئيسية في مسار التتبُّع السابق:

Kotlin

override fun onClick(v: View) {
    // The worker thread holds a lock on lockedResource
    LockTask().execute(data)

    synchronized(lockedResource) {
        // The main thread requires lockedResource here
        // but it has to wait until LockTask finishes using it.
    }
}

class LockTask : AsyncTask<Array<Int>, Int, Long>() {
    override fun doInBackground(vararg params: Array<Int>): Long? =
            synchronized(lockedResource) {
                // This is a long-running operation, which makes
                // the lock last for a long time
                BubbleSort.sort(params[0])
            }
}

Java

@Override
public void onClick(View v) {
    // The worker thread holds a lock on lockedResource
   new LockTask().execute(data);

   synchronized (lockedResource) {
       // The main thread requires lockedResource here
       // but it has to wait until LockTask finishes using it.
   }
}

public class LockTask extends AsyncTask<Integer[], Integer, Long> {
   @Override
   protected Long doInBackground(Integer[]... params) {
       synchronized (lockedResource) {
           // This is a long-running operation, which makes
           // the lock last for a long time
           BubbleSort.sort(params[0]);
       }
   }
}

مثال آخر هو سلسلة التعليمات الرئيسية لتطبيق معيَّن تنتظر نتيجة من سلسلة محادثات عامل تشغيل، كما هو موضّح في الرمز البرمجي التالي. يُرجى ملاحظة أنّ استخدام wait() وnotify() ليس نمطًا مقترَحًا في لغة Kotlin، إذ إنّ لها آليات خاصة بها للتعامل مع التزامن. عند استخدام لغة Kotlin، يجب أن تستخدم آليات خاصة بلغة Kotlin إن أمكن.

Kotlin

fun onClick(v: View) {
    val lock = java.lang.Object()
    val waitTask = WaitTask(lock)
    synchronized(lock) {
        try {
            waitTask.execute(data)
            // Wait for this worker thread’s notification
            lock.wait()
        } catch (e: InterruptedException) {
        }
    }
}

internal class WaitTask(private val lock: java.lang.Object) : AsyncTask<Array<Int>, Int, Long>() {
    override fun doInBackground(vararg params: Array<Int>): Long? {
        synchronized(lock) {
            BubbleSort.sort(params[0])
            // Finished, notify the main thread
            lock.notify()
        }
    }
}

Java

public void onClick(View v) {
   WaitTask waitTask = new WaitTask();
   synchronized (waitTask) {
       try {
           waitTask.execute(data);
           // Wait for this worker thread’s notification
           waitTask.wait();
       } catch (InterruptedException e) {}
   }
}

class WaitTask extends AsyncTask<Integer[], Integer, Long> {
   @Override
   protected Long doInBackground(Integer[]... params) {
       synchronized (this) {
           BubbleSort.sort(params[0]);
           // Finished, notify the main thread
           notify();
       }
   }
}

هناك بعض الحالات الأخرى التي يمكن أن تحظر سلسلة التعليمات الرئيسية، بما في ذلك سلاسل المحادثات التي تستخدم Lock وSemaphore، بالإضافة إلى مجموعة موارد (مثل مجموعة من اتصالات قاعدة البيانات) أو آليات الاستبعاد المتبادلة الأخرى.

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

احرص على تثبيت الأقفال لأقلّ فترة زمنية ممكنة، أو لأقل من ذلك، تقييم ما إذا كان التطبيق بحاجة إلى التعليق في المقام الأول. إذا كنت تستخدم القفل لتحديد وقت تحديث واجهة المستخدم استنادًا إلى معالجة سلسلة محادثات عاملة، استخدِم آليات مثل onProgressUpdate() وonPostExecute() للتواصل بين العامل الرئيسي وسلاسل المحادثات الرئيسية.

القيود

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

الأقفال غير محددة هي ظاهرة تمت دراستها جيدًا في علوم الكمبيوتر، وهناك خوارزميات للوقاية من العوائق يمكنك استخدامها لتجنب العوائق.

لمزيد من المعلومات، يُرجى الاطّلاع على طريقة الإغلاق وخوارزميات منع القفل على ويكيبيديا.

أجهزة استقبال البث البطيئة

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

يحدث خطأ ANR في الحالات التالية:

  • لم ينتهِ جهاز استقبال البث من تنفيذ طريقة onReceive() خلال فترة زمنية طويلة.
  • يتصل جهاز استقبال البث بـ goAsync() ولا يتصل بـ finish() بكائن PendingResult.

يجب ألا يجري تطبيقك عمليات قصيرة إلا باستخدام طريقة onReceive() من خلال BroadcastReceiver. ومع ذلك، إذا كان تطبيقك يتطلب معالجة أكثر تعقيدًا نتيجة بث الرسالة، يجب تأجيل المهمة إلى IntentService.

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

الشكل 6. المخطط الزمني لـ Traceview يعرض عمل &quot;BroadcastBroadcastr&quot; على سلسلة المحادثات الرئيسية

الشكل 6. المخطط الزمني في Traceview يعرض عمل BroadcastReceiver في سلسلة التعليمات الرئيسية

يمكن أن يحدث ذلك بسبب تنفيذ عمليات طويلة الأمد على طريقة onReceive() في BroadcastReceiver، كما هو موضّح في المثال التالي:

Kotlin

override fun onReceive(context: Context, intent: Intent) {
    // This is a long-running operation
    BubbleSort.sort(data)
}

Java

@Override
public void onReceive(Context context, Intent intent) {
    // This is a long-running operation
    BubbleSort.sort(data);
}

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

Kotlin

override fun onReceive(context: Context, intent: Intent) {
    Intent(context, MyIntentService::class.java).also { intentService ->
        // The task now runs on a worker thread.
        context.startService(intentService)
    }
}

class MyIntentService : IntentService("MyIntentService") {
    override fun onHandleIntent(intent: Intent?) {
        BubbleSort.sort(data)
    }
}

Java

@Override
public void onReceive(Context context, Intent intent) {
    // The task now runs on a worker thread.
    Intent intentService = new Intent(context, MyIntentService.class);
    context.startService(intentService);
}

public class MyIntentService extends IntentService {
   @Override
   protected void onHandleIntent(@Nullable Intent intent) {
       BubbleSort.sort(data);
   }
}

ونتيجة لاستخدام IntentService، يتم تنفيذ العملية طويلة الأمد على سلسلة التعليمات البرمجية بدلاً من سلسلة التعليمات الرئيسية. يوضح الشكل 7 العمل المؤجَّل إلى سلسلة العامل في الجدول الزمني لـ Traceview.

الشكل 7. المخطط الزمني في Traceview يعرض رسالة البث التي تمت معالجتها على سلسلة تعليمات خاصة بالعاملين

الشكل 7. المخطط الزمني في Traceview يعرض رسالة البث التي تمت معالجتها على سلسلة تعليمات خاصة بالعاملين

يمكن لجهاز استقبال البث استخدام الرمز goAsync() لإعلام النظام بأنّه يحتاج إلى مزيد من الوقت لمعالجة الرسالة. ومع ذلك، يجب استدعاء finish() على كائن PendingResult. يوضح المثال التالي كيفية استدعاء end() للسماح للنظام بإعادة تدوير جهاز استقبال البث وتجنب خطأ ANR:

Kotlin

val pendingResult = goAsync()

object : AsyncTask<Array<Int>, Int, Long>() {
    override fun doInBackground(vararg params: Array<Int>): Long? {
        // This is a long-running operation
        BubbleSort.sort(params[0])
        pendingResult.finish()
        return 0L
    }
}.execute(data)

Java

final PendingResult pendingResult = goAsync();
new AsyncTask<Integer[], Integer, Long>() {
   @Override
   protected Long doInBackground(Integer[]... params) {
       // This is a long-running operation
       BubbleSort.sort(params[0]);
       pendingResult.finish();
   }
}.execute(data);

في المقابل، إذا نقل الرمز من جهاز استقبال بث بطيء إلى سلسلة محادثات أخرى واستخدام goAsync()، لن يؤدي ذلك إلى إصلاح خطأ ANR إذا كان البث في الخلفية. لا تزال مهلة ANR سارية.

نشاط الألعاب

ساعدت مكتبة GameActivity في تقليل أخطاء ANR في دراسات حالة للألعاب والتطبيقات المكتوبة باللغتين C أو C++. وفي حال استبدال نشاطك الأصلي الحالي بـ GameActivity، يمكنك تقليل حظر سلاسل محادثات واجهة المستخدم ومنع حدوث بعض أخطاء ANR.

لمزيد من المعلومات حول أخطاء ANR، راجع الاستمرار في استجابة تطبيقك. لمزيد من المعلومات حول سلاسل المحادثات، يمكنك مراجعة أداء سلاسل المحادثات.