ANR (Application Not Responding) — اعلان سیستمی اندروید است که وقتی برنامه به مدت ۵ ثانیه به ورودی پاسخ نمیدهد ظاهر میشود. برخلاف گلیچها (خطاهای منطقی بدون مسدود کردن UI) و لگها (کندی بدون توقف کامل)، ANR یک خرابی بحرانی است که توسط سیستم عامل ثبت میشود: اندروید دیالوگ «برنامه پاسخ نمیدهد» را با پیشنهاد بستن یا صبر کردن نمایش میدهد. طبق Android Vitals Documentation، برنامههایی با نرخ ANR بالای ۰.۵٪ رتبه پایینتری در Google Play دارند و ممکن است از توصیهها پنهان شوند. تشخیص شامل تحلیل /data/anr/traces.txt، استفاده از StrictMode و پروفایلکردن نخ اصلی است.
نکات اصلی
ANR (Application Not Responding) — مکانیزم حفاظت از کاربر در اندروید است که وقتی برنامه از پاسخدهی به ورودی بازمیایستد فعال میشود. سیستم زمان پردازش رویدادها را ردیابی میکند: اگر BroadcastReceiver onReceive را در ۱۰ ثانیه تمام نکند، Service در ۲۰ ثانیه از onCreate برنگردد، و ContentProvider در ۱۵ ثانیه پاسخ ندهد — اندروید ANR تولید میکند.
وقتی ANR رخ میدهد، اندروید یک دیالوگ سیستمی بالای همه پنجرهها نشان میدهد: «برنامه پاسخ نمیدهد. آن را ببندیم یا صبر کنیم؟». کاربر میتواند برنامه را ببندد یا منتظر بازیابی آن بماند. اگر ANR مکرراً تکرار شود، کاربر برنامه را حذف میکند. Google Play نرخ ANR — درصد جلسات با ANR — را در الگوریتمهای رتبهبندی در نظر میگیرد.
در iOS معادلی برای ANR با دیالوگ سیستمی وجود ندارد. در عوض اپل از Watchdog استفاده میکند که برنامه را با کد 0x8badf00d خاتمه میدهد. کاربر دیالوگی نمیبیند — برنامه simplement به صفحه اصلی بسته میشود. این باعث میشود ANR در اندروید برای کاربر بیشتر قابل توجه باشد، اما اطلاعات بیشتری برای تشخیص به سیستم میدهد.
ANR زمانی رخ میدهد که سیستم زمانسنجی را برای یکی از چهار نوع مؤلفه ردیابی میکند. هر مؤلفه محدودیت زمانی خود را دارد.
BroadcastReceiver در نخ اصلی اجرا میشود. اگر onReceive یک درخواست شبکه همزمان، عملیات نوشتن طولانی در پایگاه داده یا انتظار برای مسدودیت راهاندازی کند — پس از ۱۰ ثانیه ANR رخ میدهد. راهحل: برای پردازش در پسزمینه از goAsync() و WorkManager استفاده کنید. سناریوی معمول — دریافت اعلان Push از FCM و ذخیره همزمان در Room.
Service.onCreate و Service.onStartCommand محدودیت ۲۰ ثانیه دارند. اگر سرویس راهاندازی سنگین (بارگذاری کتابخانهها، خواندن پیکربندی از شبکه) را در نخ اصلی انجام دهد — ANR اجتنابناپذیر است. برای اجرای تضمینی در نخ پسزمینه از IntentService (منسوخ) یا WorkManager استفاده کنید.
ContentProvider.onCreate قبل از فراخوانی Application.onCreate اجرا میشود و محدودیت ۱۵ ثانیه دارد. اگر ارائهدهنده مهاجرت پایگاه داده، بارگذاری دیکشنریها یا راهاندازی SDK از شبکه را انجام دهد — این باعث ANR هنگام راهاندازی برنامه میشود. راهحل: راهاندازی تنبل، انتقال عملیات سنگین به WorkManager.
اندروید چندین ابزار برای تحلیل ANR ارائه میدهد: از لاگهای سیستمی تا کتابخانههای تخصصی.
در هر ANR، اندروید فایل /data/anr/traces.txt را با تخلیه پشته تمام نخهای برنامه ذخیره میکند. نخ «main» را پیدا کنید — آخرین متد در پشته علت را نشان میدهد. الگوهای معمول: Thread.sleep(), InputStream.read(), BinderProxy.transact(). برای استخراج فایل از دستگاه از adb با مجوز سوپریوزر استفاده کنید.
Firebase Crashlytics به طور خودکار ANRها را جمعآوری کرده و در داشبورد به همراه ردیابی نمایش میدهد. برای Android 11+ گزارشهای ANR با پشته کامل نخ اصلی میآیند. ادغام نیاز به افزودن وابستگی و راهاندازی FirebaseApp در Application.onCreate دارد.
CPU Profiler در Android Studio به شما امکان میدهد ردیابی عملکرد برنامه را ضبط کرده و ببینید کدام متدها زمان CPU را مصرف میکنند. «Record with method traces» را فعال کنید و سناریوی ایجاد ANR را بازتولید کنید. در خط زمانی مشخص میشود که کدام متدها در لحظه هنگ در نخ اصلی اجرا میشدند.
نمونه ادغام Firebase Crashlytics برای جمعآوری ANR در اندروید:
class App : Application() {
override fun onCreate() {
super.onCreate()
FirebaseApp.initializeApp(this)
FirebaseCrashlytics.getInstance()
.setCrashlyticsCollectionEnabled(true)
StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.Builder()
.detectAll()
.penaltyLog()
.build())
}
}
رفع ANR قبل از هر چیز انتقال تمام عملیات طولانی از نخ اصلی به نخهای پسزمینه است. بیایید تکنیکهای خاص برای هر نوع مؤلفه را بررسی کنیم.
WorkManager — راهحل توصیه شده Google برای کار در پسزمینه است. اجرای وظیفه را در نخ پسزمینه با در نظر گرفتن وضعیت دستگاه تضمین میکند. برخلاف Service، WorkManager نخ اصلی را مسدود نمیکند و در برابر راهاندازی مجدد برنامه مقاوم است. برای BroadcastReceiver از goAsync() استفاده کنید و نتیجه PendingResult را به WorkManager منتقل کنید.
تمام درخواستهای شبکه، کار با پایگاه داده و عملیات فایل را با Dispatchers.IO اجرا کنید. نخ اصلی فقط باید UI را بهروزرسانی کند. برای لغو خودکار کوروتینها هنگام نابودی Activity از viewModelScope استفاده کنید. از runBlocking() در هر زمینهای خودداری کنید — این یک مسدودیت همزمان نخ جاری است.
اگر ContentProvider راهاندازی طولانی انجام میدهد، از مکانیزم بارگذاری تأخیری استفاده کنید: ارائهدهندهای ایجاد کنید که فوراً داده را برمیگرداند، و راهاندازی سنگین را از طریق WorkManager با تأخیر اجرا کنید. این کار از ANR هنگام راهاندازی برنامه جلوگیری میکند، زمانی که سیستم بیشترین حساسیت را به تأخیر دارد.
نمونه استفاده صحیح از BroadcastReceiver با goAsync در اندروید:
class FcmReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
val pendingResult = goAsync()
WorkManager.getInstance(context!!)
.enqueue(OneTimeWorkRequest.from(NotificationWorker::class.java))
pendingResult.finish()
}
}
بهترین راه مبارزه با ANR جلوگیری از ظهور آنها در مرحله توسعه از طریق ابزارها و راهحلهای معماری است.
StrictMode با سیاستهای detectNetwork() و detectDiskReads()/detectDiskWrites() فعال شده، ANRهای بالقوه را در مرحله توسعه شناسایی میکند. در ساخت Debug penaltyDeath را تنظیم کنید — هر نقضی منجر به سقوط فوری میشود و توسعهدهنده مشکل را قبل از commit میبیند.
Firebase Performance زمان اجرای عملیات کلیدی را ردیابی کرده و نشان میدهد کدام سناریوها از آستانه ANR فراتر میروند. ردیابی سفارشی برای هر صفحه و درخواست شبکه تنظیم کنید. اگر زمان اجرا از ۳ ثانیه بیشتر شود — این یک ANR بالقوه نیازمند بهینهسازی است.
شرایط کند را شبیهسازی کنید: سرعت شبکه را از طریق Network Link Conditioner در iOS یا Android Emulator محدود کنید. خواندن از دیسک را از طریق شبیهسازی حافظه کند کاهش دهید. ANR اغلب دقیقاً در چنین شرایطی ظاهر میشود و در دستگاههای سریع توسعهدهنده قابل مشاهده نیست.
سوالات متداول
اندروید به صراحت زمان پردازش رویدادها را در نخ اصلی ردیابی کرده و دیالوگ ANR را نشان میدهد. iOS از Watchdog استفاده میکند که پس از ۱۰–۲۰ ثانیه هنگ برنامه را به زور میبندد. ANR ویژگی معماری اندروید است که در آن چندین مؤلفه (BroadcastReceiver, Service) محدودیتهای زمانی سخت دارند.
در Android 11+ میتوان تخلیه ANR را از طریق adb shell dumpsys dropbox --print data_app_anr دریافت کرد. در Android 10 و پایینتر بدون root دسترسی به /data/anr/traces.txt وجود ندارد. از Firebase Crashlytics استفاده کنید — به طور خودکار گزارشهای ANR را برای Android 11+ جمعآوری میکند.
Google Play نرخ ANR کمتر از ۰.۵٪ را توصیه میکند — یعنی حداکثر ۵ ANR در ۱۰۰۰ جلسه. برنامه با نرخ بالای ۱٪ اخطار در Google Play Console دریافت میکند و ممکن است از توصیهها پنهان شود. در حالت ایدهآل نرخ ANR باید زیر ۰.۱٪ باشد.
کوروتین به خودی خود نخ را مسدود نمیکند. اما اگر در داخل کوروتین runBlocking در نخ اصلی اجرا شود یا کوروتین با Dispatchers.Main راهاندازی شده و عملیات طولانی CPU انجام دهد — این باعث ANR میشود. برای ورودی-خروجی از Dispatchers.IO و برای محاسبات از Dispatchers.Default استفاده کنید.
از Android Emulator با پروفایل «Slow Network» استفاده کنید یا تستی بنویسید که Thread.sleep(6000) را در نخ اصلی فراخوانی کند. برنامه را از طریق Debug اجرا کنید و بعد از ۵ ثانیه دیالوگ ANR را خواهید دید. بررسی کنید که در logcat یک رکورد ANR با ردیابی ظاهر شده است.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.