ANR (Application Not Responding) — یک اعلان سیستمی اندروید است که هنگامی ظاهر میشود که برنامه بیش از ۵ ثانیه به ورودی کاربر پاسخ نمیدهد. به گفته Android Developers، علت اصلی عملیات طولانی در رشته اصلی است که پردازش لمسها و رندر رابط را مسدود میکند. درک مکانیزمهای ANR برای هر توسعهدهنده اندروید جهت ایجاد برنامههای پاسخگو ضروری است.
نکات اصلی
ANR (Application Not Responding) — یک کادر محاورهای سیستم عامل اندروید است که وقتی برنامه از پاسخ به ورودی کاربر بازمیایستد ظاهر میشود. سیستم زمان پردازش رویدادها را از طریق InputDispatcher ردیابی میکند: اگر لمس یا فشار دکمه در عرض ۵ ثانیه پردازش نشود، اندروید کادر محاورهای با پیشنهاد بستن یا منتظر ماندن برنامه نشان میدهد.
مکانیزم ANR از تجربه کاربری در برابر برنامههای هنگ کرده محافظت میکند. اندروید به یک برنامه اجازه نمیدهد کل سیستم را مسدود کند — برخلاف سیستمهای دسکتاپ، پلتفرم موبایل به اجبار زمان پردازش رویدادها را محدود میکند. BroadcastReceiver محدودیت ۱۰ ثانیه و سرویس foreground محدودیت ۲۰ ثانیه دارد.
ANR یک استثنا در کد نیست — این یک مکانیزم سیستمی در سطح فرآیندهای لینوکس است. اندروید سیگنال SIGQUIT را به فرآیند میفرستد، پس از آن سیستم پشته فراخوانی همه رشتهها را در فایل traces.txt ذخیره میکند. توسعهدهنده ANR را نه به عنوان یک استثنای catch، بلکه به عنوان گزارشی پس از راهاندازی مجدد برنامه دریافت میکند. در اندروید ۱۱+ API ApplicationExitInfo ظاهر شد که به شما امکان میدهد علت خاتمه فرآیند از جمله ANR را به صورت برنامهریزی شده به دست آورید — این کار جمعآوری آمار را بدون تجزیه دستی traces.txt ساده میکند.
پنج دسته عملیات به طور پایدار به ANR در برنامههای اندروید منجر میشوند. هر یک از آنها رشته اصلی را مسدود میکند و به سیستم اجازه پردازش رویدادهای ورودی و بازترسیم صفحه را نمیدهد.
درخواستهای HTTP همزمان که در رشته UI اجرا میشوند — شایعترین علت ANR در توسعهدهندگان مبتدی. حتی یک درخواست سریع به سرور ممکن است ۱–۳ ثانیه طول بکشد و در اتصال ضعیف — ۳۰ ثانیه یا بیشتر. اندروید از API 11 به بعد به صراحت عملیات شبکه را در رشته اصلی ممنوع میکند و NetworkOnMainThreadException را پرتاب میکند.
برای فراخوانیهای ناهمگام از Coroutines یا RxJava استفاده کنید. کوروتینها با Dispatchers.IO درخواست را در رشته پسزمینه اجرا میکنند و نتیجه را از طریق Dispatchers.Main به رشته اصلی منتقل میکنند. این امر مسدود شدن رشته UI توسط عملیات شبکه را کاملاً از بین میبرد.
fun fetchUserData() {
CoroutineScope(Dispatchers.Main).launch {
val result = withContext(Dispatchers.IO) {
api.getUserData() // عملیات پسزمینه
}
updateUI(result) // نتیجه در رشته اصلی
}
}
پردازش آرایههای بزرگ داده، تجزیه JSON یا XML، کار مستقیم با bitmap در رشته اصلی — دومین علت شایع ANR. حتی ۳۰۰ میلیثانیه کار مداوم رشته UI بدون بازگشت به حلقه رویداد باعث تأخیر قابل توجه در رندر میشود و مقدار مرزی ۵ ثانیه به عنوان ANR ثبت میشود.
WorkManager و سرویسهای پسزمینه برای انتقال محاسبات سنگین از رشته اصلی طراحی شدهاند. برای انتقال دادهها به صورت تکهای بدون مسدود کردن UI از AsyncTask (منسوخ شده)، ListenableFuture یا Kotlin Flow استفاده کنید.
بنبست (Deadlock) زمانی رخ میدهد که دو رشته قفلها را نگه داشته و منتظر یکدیگر باشند. اگر یکی از رشتهها اصلی باشد، سیستم دقیقاً پس از ۵ ثانیه ANR را ثبت میکند. Thread.join()، CountDownLatch.await() و بلوکهای synchronized که از رشته UI فراخوانی میشوند خطر بنبست را به همراه دارند.
از هرگونه عملیات مسدودکننده در رشته اصلی خودداری کنید. به جای synchronized از ConcurrentHashMap، به جای Thread.join() از کوروتینها با async/await استفاده کنید. این قانون برای هر زبانی در اندروید معتبر است: Java، Kotlin یا C++ از طریق JNI.
BroadcastReceiver به طور پیشفرض در رشته اصلی اجرا میشود. اگر onReceive() بیش از ۱۰ ثانیه مشغول باشد، اندروید ANR را نشان میدهد. بارگذاری داده از پایگاه داده یا شبکه درون onReceive راهی تضمینی به سوی هنگ است.
درون BroadcastReceiver برای تغییر به رشته پسزمینه از goAsync() یا registerReceiver با getBackgroundBroadcastReceiver() استفاده کنید. این امکان پردازش رویدادها را بدون مسدود کردن UI فراهم میکند.
پرسوجوهای سنگین به ContentProvider یا کار مستقیم با SQLite در رشته UI — علت کمتر آشکار اما شایع ANR. در هنگام مهاجرت پایگاه داده یا درج انبوه هزاران رکورد، زمان اجرا ممکن است از محدودیت ۵ ثانیه فراتر رود.
تمام عملیات پایگاه داده را از طریق Room با توابع suspend به رشتههای پسزمینه منتقل کنید. Room به طور خودکار بررسی میکند که پرسوجو در رشته اصلی اجرا نشود و در صورت نقض استثنا پرتاب میکند.
تشخیص ANR با اشکالزدایی استثناهای معمولی متفاوت است — شما نمیتوانید ANR را در try-catch بگیرید. منبع اصلی اطلاعات فایل traces.txt است که اندروید در لحظه هنگ ایجاد میکند.
traces.txt حاوی پشته فراخوانی همه رشتههای برنامه در لحظه ANR است. برای خواندن فایل از یک دستگاه واقعی، دستور adb bugreport را اجرا کنید که گزارش کامل سیستم شامل تمام ANRهای اخیر را جمعآوری میکند. برای شبیهساز، فایل در /data/anr/traces.txt در دسترس است. پشته فراخوانی نشان میدهد که کدام متد در لحظه مسدود شدن در رشته اصلی اجرا میشد.
adb bugreport bugreport.zip
unzip -p bugreport.zip "*traces*" > traces.txt
Google Play Console بخش ANR & Crash را با گزارشهای تجمیعشده و فراوانی خطاها ارائه میدهد. برای هر ANR پشته فراخوانی و آمار بر اساس دستگاهها نشان داده میشود: مدل، نسخه اندروید، منطقه. این امکان شناسایی ANRهایی را که به دستگاهها یا نسخههای خاص سیستم وابسته هستند فراهم میکند.
Android Studio از سال ۲۰۲۱ شامل ANR Watchdog در پروفایلر است. اگر رشته اصلی بیش از زمان آستانه پاسخ ندهد، به طور خودکار dump رشتهها را ثبت میکند. ابزار خط زمانی رویدادها را نشان میدهد: چه عملیاتی راهاندازی شده، چه متدهایی اجرا شده و مسدود شدن در چه مرحلهای رخ داده است.
پیشگیری ANR بر یک قانون اساسی استوار است: رشته اصلی باید فقط رویدادهای UI را پردازش کند. هر عملیاتی که بیش از ۱۶ میلیثانیه (زمان یک فریم) طول بکشد باید در رشته پسزمینه اجرا شود.
StrictMode — ابزار داخلی اندروید برای تشخیص ANRهای بالقوه در مرحله توسعه. آن را در Application.onCreate() با پرچمهایی برای عملیات دیسڥ و شبکه فعال کنید. در صورت نقض، StrictMode استثنا پرتاب میکند یا در logcat مینویسد.
if (BuildConfig.DEBUG) {
StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectNetwork()
.penaltyLog()
.build()
.let { StrictMode.setThreadPolicy(it) }
}
Kotlin Coroutines — روش استاندارد کار ناهمگام در برنامههای مدرن اندروید. اصل اساسی: عملیات ورودی-خروجی روی Dispatchers.IO اجرا میشوند، نتیجه برای بهروزرسانی UI به Dispatchers.Main منتقل میشود. برای سناریوهای Flow از Dispatchers.Default برای وظایف فشرده CPU استفاده کنید.
RxJava در پروژههای قدیمی محبوب باقی میماند. subscribeOn(Schedulers.io()) و observeOn(AndroidSchedulers.mainThread()) — حداقل مجموعه برای جلوگیری از ANR. قانون اصلی یکسان است: هیچ Observable یا Flowable نباید دادهها را از رشته اصلی emit کند.
Firebase Crashlytics از نسخه SDK 18.4.0 نظارت ANR را خارج از جعبه
پشتیبانی میکند. برای اندروید ۱۱+ Crashlytics از API سیستمی ApplicationExitInfo استفاده میکند که علت دقیق خاتمه را ارائه میدهد: ANR، Crash یا کشته شدن توسط سیستم. کلیدهای سفارشی با پارامترهای صفحه و وضعیت را برای تحلیل زمینهای فعال کنید.
پنج ابزار تمام مراحل کار با 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 زمان پاسخ رشته UI را ردیابی میکند و به طور خودکار برای عملیات مشکوک طولانی رهگیری ایجاد میکند. اگر رشته اصلی بیش از ۵۰۰ میلیثانیه مسدود شود، Performance یک رهگیری سفارشی با نام متد مقصر ثبت میکند. این امکان تشخیص سناریوهای ANR را بدون مشارکت کاربر و قبل از بحرانی شدن آنها فراهم میکند.
ادغام با Firebase Crashlytics تصویر کاملی میدهد: Performance کندیهای قبل از ANR را نشان میدهد و Crashlytics — خود واقعیت هنگ. در Firebase Console برای رویداد ANR rate بالای ۰٫۱٪ هشدار تنظیم کنید و قبل از شکایتهای جمعی کاربران از مشکلات جدید مطلع خواهید شد.
سؤالات متداول
ANR — یک هنگ است که در آن برنامه پاسخ نمیدهد اما در حافظه باقی میماند. Crash — پایان کامل اضطراری با خروج از فرآیند. ANR را میتوان «تحمل کرد» اگر سیستم یا کاربر منتظر پاسخ بمانند، اما Crash همیشه برنامه را خاتمه میدهد.
خیر. ANR یک استثنای Java/Kotlin نیست، بلکه یک سیگنال سیستم در سطح فرآیندها است (SIGQUIT). توسعهدهنده نمیتواند آن را در کد برنامه پردازش کند. تنها راه واکنش به ANR — تجزیه و تحلیل گزارشها پس از راهاندازی مجدد است.
عملکرد دستگاه، نسخه اندروید، بار CPU و تعداد فرآیندهای پسزمینه بر احتمال ANR تأثیر میگذارند. در دستگاههای ضعیف، همان عملیات ممکن است ۲–۳ برابر بیشتر طول بکشد و از محدودیت ۵ ثانیه فراتر رود.
۱۰ ثانیه برای BroadcastReceiver معمولی در onReceive(). برای سرویسهای foreground محدودیت ۲۰ ثانیه و برای ContentProvider محدودیت صریحی وجود ندارد، اما مسدود شدن رشته اصلی بیش از ۵ ثانیه همچنان ANR ایجاد میکند.
در تمام buildهای debug StrictMode را فعال کنید، نظارت را از طریق Firebase Crashlytics اضافه کنید و هنگام وقوع ANR از adb bugreport استفاده کنید. ANRهای نامنظم اغلب با شرایط رقابت (race condition) یا وضعیتهای خاص شبکه مرتبط هستند.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید