ANR في تطوير Android — ما هو، أسبابه وطرق إصلاحه

المؤلف: IT Sectr نُشر: 2026-03-28 وقت القراءة: 9 دق

ANR (Application Not Responding) هو إشعار نظام Android يظهر عندما يتوقف التطبيق عن الاستجابة لإدخال المستخدم لأكثر من 5 ثوانٍ. وفقًا لـ Android Developers، السبب الرئيسي هو العمليات الطويلة على الخيط الرئيسي التي تمنع معالجة اللمس وعرض الواجهة. فهم آليات ANR ضروري لكل مطور Android لإنشاء تطبيقات سريعة الاستجابة.

الخلاصة

  • ANR — تحذير نظام Android عند تجميد التطبيق لأكثر من 5 ثوانٍ
  • الخيط الرئيسي (خيط UI) — المكان الوحيد الذي يؤدي فيه الحظر إلى ANR
  • InputDispatcher — مكون النظام الذي يكتشف تأخير الإدخال ويطلق ANR
  • traces.txt — الملف الرئيسي لتشخيص سبب التجميد على الجهاز
  • StrictMode — أداة Android مدمجة لاكتشاف العمليات الطويلة على خيط UI

ما هو ANR

ANR (Application Not Responding) هو مربع حوار لنظام التشغيل Android يظهر عندما يتوقف التطبيق عن الاستجابة لإدخال المستخدم. يتتبع النظام وقت معالجة الأحداث من خلال InputDispatcher: إذا لم تتم معالجة لمسة أو ضغطة زر خلال 5 ثوانٍ، يعرض Android حوارًا يطلب إغلاق التطبيق أو الانتظار.

آلية ANR تحمي تجربة المستخدم من التطبيقات المجمدة. لا يسمح Android لتطبيق واحد بحظر النظام بأكمله — على عكس أنظمة سطح المكتب، تحد المنصة المحمولة إجباريًا وقت معالجة الأحداث. BroadcastReceiver لديه حد 10 ثوانٍ، والخدمة الأمامية لديها 20 ثانية.

ANR ليس استثناءً في الكود — إنها آلية نظام على مستوى عمليات Linux. يرسل Android إشارة SIGQUIT إلى العملية، وبعدها يحفظ النظام مكدس استدعاءات جميع الخيوط في ملف traces.txt. لا يتلقى المطور ANR كاستثناء catch، بل كتقرير بعد إعادة تشغيل التطبيق. على Android 11+، تسمح واجهة برمجة التطبيقات ApplicationExitInfo بالحصول برمجيًا على سبب إنهاء العملية، بما في ذلك ANR — مما يبسط جمع الإحصائيات دون الحاجة إلى تحليل traces.txt يدويًا.

الأسباب الرئيسية لـ ANR

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

طلبات الشبكة على الخيط الرئيسي

طلبات HTTP المتزامنة المنفذة على خيط UI هي السبب الأكثر شيوعًا لـ ANR بين المطورين المبتدئين. حتى الطلب السريع للخادم يمكن أن يستغرق 1–3 ثوانٍ، ومع اتصال ضعيف — 30 ثانية أو أكثر. يمنع Android صراحة عمليات الشبكة على الخيط الرئيسي بدءًا من API 11، مما يطرح NetworkOnMainThreadException.

استخدم Coroutines أو RxJava للاستدعاءات غير المتزامنة. تنفذ coroutines مع dispatcher Dispatchers.IO الطلب في خلفية وتنقل النتيجة إلى الخيط الرئيسي عبر Dispatchers.Main. هذا يلغي تمامًا حظر خيط UI بواسطة عمليات الشبكة.

kotlin
fun fetchUserData() {
    CoroutineScope(Dispatchers.Main).launch {
        val result = withContext(Dispatchers.IO) {
            api.getUserData() // عملية في الخلفية
        }
        updateUI(result) // النتيجة على الخيط الرئيسي
    }
}

الحسابات المكثفة على خيط UI

معالجة مصفوفات البيانات الكبيرة، تحليل JSON أو XML، العمل مع الصور النقطية مباشرة على الخيط الرئيسي — ثاني أكثر أسباب ANR شيوعًا. حتى 300 مللي ثانية من العمل المستمر لخيط UI دون العودة إلى حلقة الأحداث تسبب تأخيرًا ملحوظًا في العرض، وحد الـ 5 ثوانٍ يُسجل كـ ANR.

WorkManager والخدمات الخلفية مصممة لنقل الحسابات الثقيلة خارج الخيط الرئيسي. استخدم AsyncTask (قديم)، ListenableFuture أو Kotlin Flow لنقل البيانات على دفعات دون حظر واجهة المستخدم.

أقفال المزامنة و Deadlock

يحدث Deadlock عندما يحمل خيطان أقفالًا وينتظر كل منهما الآخر. إذا كان أحد الخيوط هو الخيط الرئيسي، يسجل النظام ANR بعد 5 ثوانٍ بالضبط. Thread.join() و CountDownLatch.await() وكتل synchronized المستدعاة من خيط UI تحمل خطر الحظر.

تجنب أي عمليات حظر على الخيط الرئيسي. بدلاً من synchronized، استخدم ConcurrentHashMap؛ بدلاً من Thread.join() — coroutines مع async/await. تنطبق هذه القاعدة على أي لغة في Android: Java أو Kotlin أو C++ عبر JNI.

BroadcastReceiver طويل الأمد

يتم تنفيذ BroadcastReceiver على الخيط الرئيسي افتراضيًا. إذا كان onReceive() مشغولاً لأكثر من 10 ثوانٍ، يعرض Android ANR. تحميل البيانات من قاعدة البيانات أو الشبكة داخل onReceive هو طريق مضمون للتجميد.

استخدم goAsync() داخل BroadcastReceiver للتبديل إلى خيط خلفية، أو registerReceiver مع getBackgroundBroadcastReceiver(). هذا يسمح بمعالجة الأحداث دون حظر واجهة المستخدم.

ContentProvider و SQLite على الخيط الرئيسي

استعلامات ثقيلة لـ ContentProvider أو العمل المباشر مع SQLite على خيط UI — سبب أقل وضوحًا ولكنه شائع لـ ANR. أثناء ترحيل قاعدة البيانات أو الإدراج الجماعي لآلاف السجلات، قد يتجاوز وقت التنفيذ حد الـ 5 ثوانٍ.

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

كيفية تشخيص ANR

تشخيص ANR يختلف عن تصحيح الاستثناءات العادية — لا يمكنك التقاط ANR في كتلة try-catch. مصدر المعلومات الرئيسي هو ملف traces.txt، الذي ينشئه Android في لحظة التجميد.

traces.txt يحتوي على مكدس استدعاءات جميع خيوط التطبيق في وقت ANR. لقراءة الملف من جهاز حقيقي، نفذ الأمر adb bugreport، الذي يجمع تقريرًا كاملاً للنظام بما في ذلك جميع ANRs الأخيرة. للمحاكي، الملف متاح في /data/anr/traces.txt. يظهر مكدس الاستدعاءات أي طريقة كانت تُنفذ على الخيط الرئيسي في لحظة الحظر.

text
adb bugreport bugreport.zip
unzip -p bugreport.zip "*traces*" > traces.txt

Google Play Console توفر قسم ANR & Crash مع تقارير مجمعة وتكرارات الأخطاء. لكل ANR، يُظهر مكدس الاستدعاءات وإحصائيات الجهاز: الطراز، إصدار Android، المنطقة. هذا يسمح بتحديد ANRs التي تعتمد على أجهزة أو إصدارات نظام محددة.

Android Studio منذ 2021 يحتوي على ANR Watchdog في الموصف. يسجل تلقائيًا تفريغات الخيوط إذا لم يستجب الخيط الرئيسي لأكثر من وقت حدي. تظهر الأداة خطًا زمنيًا للأحداث: أي العمليات بدأت، وأي الطرق نُفذت، وفي أي مرحلة حدث الحظر.

كيفية منع ANR

تستند الوقاية من ANR إلى قاعدة أساسية واحدة: يجب أن يعالج الخيط الرئيسي أحداث UI فقط. أي عملية أطول من 16 مللي ثانية (وقت إطار واحد) يجب أن تُنفذ في خيط خلفية.

StrictMode — التحقق التلقائي

StrictMode أداة Android مدمجة لاكتشاف ANRs المحتملة أثناء التطوير. فعّله في Application.onCreate() مع علامات لعمليات القرص والشبكة. عند المخالفة، يطرح StrictMode استثناءً أو يكتب في logcat.

kotlin
if (BuildConfig.DEBUG) {
    StrictMode.ThreadPolicy.Builder()
        .detectDiskReads()
        .detectDiskWrites()
        .detectNetwork()
        .penaltyLog()
        .build()
        .let { StrictMode.setThreadPolicy(it) }
}

الأنماط غير المتزامنة: Coroutines و RxJava

Kotlin Coroutines — الطريقة القياسية للعمل غير المتزامن في تطبيقات Android الحديثة. النهج الأساسي: عمليات الإدخال/الإخراج تُنفذ على Dispatchers.IO، النتيجة تُنقل إلى Dispatchers.Main لتحديثات UI. لسيناريوهات Flow، استخدم Dispatchers.Default للمهام كثيفة المعالجة.

RxJava يبقى شائعًا في المشاريع القديمة. subscribeOn(Schedulers.io()) و observeOn(AndroidSchedulers.mainThread()) — الحد الأدنى لمنع ANR. القاعدة الرئيسية نفسها: لا يجب أن يصدر أي Observable أو Flowable بيانات من الخيط الرئيسي.

المراقبة في الإنتاج

Firebase Crashlytics منذ إصدار SDK 18.4.0 يدعم مراقبة ANR مباشرة. لنظام Android 11+، يستخدم Crashlytics واجهة برمجة التطبيقات النظامية ApplicationExitInfo التي توفر سبب الإنهاء الدقيق: ANR، Crash، أو قتل النظام. فعّل مفاتيح مخصصة مع معلمات الشاشة والحالة للتحليل السياقي.

أدوات اكتشاف ANR

خمس أدوات تغطي جميع مراحل العمل مع ANR: من التصحيح على محطة العمل إلى المراقبة في الإنتاج. كل أداة تحل مهمتها الخاصة وتوفر بيانات لسيناريوهات مختلفة.

الأداةالغرضتنسيق البيانات
StrictModeالاكتشاف أثناء التطويرLogcat / Exception
ANR Watchdog (Android Studio)التتبع في الوقت الفعليThread dump + timeline
Google Play Consoleإحصائيات مجمعةANR rate + stack traces
Firebase Crashlyticsمراقبة الإنتاجApplicationExitInfo
adb bugreportتقرير نظام كاملtraces.txt + logcat + dmesg

كل أداة لها مجالها الخاص: StrictMode يلتقط المخالفات الواضحة في المراحل المبكرة، Crashlytics يظهر التكرار الحقيقي لـ ANR بين المستخدمين، و adb bugreport يوفر الصورة الأكثر اكتمالاً للحالات المعقدة. ادمجها للتغطية الكاملة.

Firebase Performance Monitoring

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

التكامل مع Firebase Crashlytics يعطي صورة كاملة: Performance يظهر التباطؤ قبل ANR، و Crashlytics يظهر التجميد نفسه. قم بإعداد تنبيهات في Firebase Console لمعدل ANR أعلى من 0.1%، وستتلقى إشعارات حول المشكلات الجديدة قبل شكاوى المستخدمين الجماعية.

الأسئلة الشائعة

كيف يختلف ANR عن Crash؟

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

هل يمكن التقاط ANR عبر try-catch؟

لا. ANR ليس استثناء Java/Kotlin، بل إشارة نظام على مستوى العمليات (SIGQUIT). لا يمكن للمطور معالجته في كود التطبيق. الطريقة الوحيدة للاستجابة لـ ANR هي تحليل التقارير بعد إعادة التشغيل.

لماذا يظهر ANR على بعض الأجهزة وليس على أخرى؟

أداء الجهاز، إصدار Android، تحميل CPU وعدد العمليات الخلفية تؤثر على احتمالية ANR. على الأجهزة الضعيفة، نفس العملية قد تستغرق 2–3 مرات أطول، متجاوزة حد 5 ثوانٍ.

ما هو حد وقت BroadcastReceiver قبل ANR؟

10 ثوانٍ لـ BroadcastReceiver عادي في onReceive(). للخدمات الأمامية، الحد هو 20 ثانية، ولـ ContentProvider — لا يوجد حد صريح، لكن حظر الخيط الرئيسي لأكثر من 5 ثوانٍ لا يزال يسبب ANR.

ماذا تفعل إذا حدث ANR نادرًا ولا يمكن إعادة إنتاجه؟

فعّل StrictMode في جميع إصدارات التصحيح، أضف مراقبة عبر Firebase Crashlytics، واستخدم adb bugreport عندما يحدث ANR. ANRs غير المنتظمة غالبًا ما ترتبط بظروف تسابق أو حالات شبكة محددة.

الملخص

  • ANR — آلية نظام Android تنشط عند حظر الخيط الرئيسي لأكثر من 5 ثوانٍ
  • الخيط الرئيسي يجب أن يتعامل فقط مع UI — جميع العمليات الأخرى تُنقل إلى خيوط خلفية
  • تشخيص ANR يتم عبر traces.txt و Google Play Console و Firebase Crashlytics
  • StrictMode يكتشف ANRs المحتملة أثناء التطوير دون تشغيل على جهاز حقيقي
  • Coroutines مع Dispatchers.IO — الطريقة القياسية للعمل غير المتزامن في مشاريع Android الحديثة
  • BroadcastReceiver يتطلب goAsync() أو مسجل خلفية للعمل لأكثر من 10 ثوانٍ
  • ANR في الإنتاج يُراقب عبر Crashlytics وواجهة برمجة التطبيقات المدمجة ApplicationExitInfo على Android 11 وما فوق

سنقوم بتطوير تطبيق جوال جاهز

تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.

مناقشة المشروع

اقرأ أيضًا