تعداد خطاهای «برنامه پاسخ نمی‌دهد»

وقتی رشته میانای کاربر برنامه Android برای مدت طولانی مسدود شود، خطای «برنامه پاسخ نمی‌دهد» (ANR) راه‌اندازی می‌شود. اگر برنامه در پیش‌زمینه باشد، سیستم کادر گفتگویی را به کاربر نشان می‌دهد، همان‌طور که در شکل ۱ نشان داده شده است. کادر گفتگوی ANR به کاربر این امکان را می‌دهد که برنامه را به‌اجبار ببندد.

کادر گفتگوی ANR به کاربر نشان داده شد.
شکل ۱. کادر گفتگوی ANR به کاربر نشان داده شد

خطاهای «برنامه پاسخ نمی‌دهد» مشکل‌ساز هستند زیرا رشته اصلی برنامه که مسئول به‌روزرسانی واسط کاربر است نمی‌تواند رویدادهای ورودی کاربر را پردازش کند یا ترسیم کند، که این امر باعث نارضایتی کاربر می‌شود. برای اطلاعات بیشتر درباره رشته اصلی برنامه، به نمای کلی فرایندها و رشته‌ها مراجعه کنید.

وقتی یکی از شرایط زیر رخ دهد، ANR برای برنامه‌تان راه‌اندازی می‌شود:

  • زمان ارسال ورودی تمام شده است: اگر برنامه‌تان ظرف ۵ ثانیه به رویداد ورودی (مانند فشار دادن کلید یا لمس صفحه) پاسخ نداده باشد.
  • اجرای سرویس: اگر سرویسی که برنامه‌تان اعلام کرده است نتواند اجرای Service.onCreate و Service.onStartCommand/Service.onBind را ظرف چند ثانیه به‌پایان برساند.
  • Service.startForeground فراخوانی نشده است: اگر برنامه شما از Context.startForegroundService برای شروع سرویس جدیدی در پیش‌زمینه استفاده می‌کند اما سرویس startForeground را ظرف ۵ ثانیه فراخوانی نمی‌کند.
  • پخش هدف: اگر BroadcastReceiver در مدت زمان تعیین‌شده اجرا نشود. اگر برنامه فعالیتی در پیش‌زمینه داشته باشد، این مهلت زمانی ۵ ثانیه است.
  • تعاملات JobScheduler: اگر JobService ظرف چند ثانیه از JobService.onStartJob یا JobService.onStopJob برنگردد، یا اگر کار شروع‌شده توسط کاربر شروع شود و برنامه شما ظرف چند ثانیه پس‌از فراخوانی JobService.onStartJob، JobService.setNotification را فراخوانی نکند. برای برنامه‌هایی که Android 13 و نسخه‌های پایین‌تر را هدف قرار می‌دهند، خطاهای ANR بی‌صدا هستند و به برنامه گزارش نمی‌شوند. برای برنامه‌هایی که Android 14 و نسخه‌های بالاتر را هدف قرار می‌دهند، خطاهای ANR صریح هستند و به برنامه گزارش می‌شوند.

اگر برنامه‌تان با خطای «برنامه پاسخ نمی‌دهد» مواجه است، می‌توانید از راهنمایی‌های این سند برای تشخیص و رفع مشکل استفاده کنید.

تشخیص خطاهای ANR

هنگام تشخیص ANR، چند الگوی رایج وجود دارد که باید به‌دنبال آن‌ها باشید:

  • برنامه درحال انجام عملیات آهسته شامل ورودی/خروجی در رشته اصلی است.
  • برنامه درحال انجام محاسباتی طولانی در رشته اصلی است.
  • رشته اصلی درحال انجام فراخوانی همگام‌سازی Binder به فرایند دیگری است و آن فرایند دیگر زمان زیادی برای بازگشت صرف می‌کند.
  • رشته اصلی مسدود شده است و منتظر بلوک همگام‌سازی‌شده برای عملیات طولانی است که در رشته دیگری درحال انجام است.
  • رشته اصلی با رشته دیگری در بن‌بست است، چه در فرایند شما یا ازطریق فراخوانی بایندر. رشته اصلی فقط منتظر نیست تا عملیات طولانی به‌پایان برسد، بلکه در وضعیت بن‌بست قرار دارد.

روش‌های زیر می‌تواند به شما کمک کند علت خطاهای ANR را تعیین کنید.

HealthStats

HealthStats با ضبط کل زمان کاربر و سیستم، زمان واحد پردازش مرکزی، شبکه، آمار رادیو، زمان روشن/خاموش بودن صفحه‌نمایش، و زنگ‌های بیدارباش، سنجه‌هایی درباره سلامت برنامه ارائه می‌دهد. این کار می‌تواند به شما کمک کند تا مصرف کلی CPU و تخلیه باتری را اندازه‌گیری کنید.

اشکال‌زدایی کردن

Debug به شما کمک می‌کند برنامه‌های Android را درطول توسعه بررسی کنید، ازجمله ردیابی و شمارش تخصیص برای شناسایی لرزش و تأخیر در برنامه‌ها. همچنین می‌توانید از Debug برای دریافت شمارنده‌های حافظه بومی و زمان اجرا، و سنجه‌های حافظه استفاده کنید که می‌توانند به شما در شناسایی ردپای حافظه یک فرایند خاص کمک کنند.

ApplicationExitInfo

ApplicationExitInfo در Android نسخه ۱۱ (سطح میانای برنامه‌سازی کاربردی ۳۰) یا بالاتر دردسترس است و اطلاعاتی درباره دلیل خروج از برنامه ارائه می‌دهد. این شامل خطاهای ANR، حافظه کم، خرابی‌های برنامه، استفاده بیش‌ازحد از CPU، وقفه‌های کاربر، وقفه‌های سیستم، و تغییرات اجازه زمان اجرا می‌شود.

حالت سخت‌گیرانه

استفاده از StrictMode به شما کمک می‌کند تا عملیات ورودی/خروجی تصادفی را در رشته اصلی درحین توسعه برنامه‌تان پیدا کنید. می‌توانید از StrictMode در سطح برنامه یا فعالیت استفاده کنید.

فعال کردن کادرهای گفتگوی ANR پس‌زمینه

Android کادرهای گفتگوی ANR را برای برنامه‌هایی که پردازش پیام همه‌فرستی در آن‌ها بیش‌ازحد طول می‌کشد فقط درصورتی نشان می‌دهد که نمایش همه ANRها در گزینه‌های توسعه‌دهندگان دستگاه فعال باشد. به همین دلیل، کادرهای گفتگوی ANR پس‌زمینه همیشه به کاربر نشان داده نمی‌شود، حتی وقتی برنامه با مشکل عملکرد مواجه است.

گلوگاه‌های بازسازی ترکیبی

برای ردیابی گلوگاه‌های بازترکیب، از تحلیل‌گر استودیو Android و بازرس چیدمان استفاده کنید. برای اطلاعات بیشتر، به عملکرد Jetpack Compose مراجعه کنید.

کشیدن فایل ردیابی

وقتی Android با ANR مواجه می‌شود، اطلاعات ردیابی را ذخیره می‌کند. در نسخه‌های قدیمی‌تر سیستم‌عامل، یک فایل /data/anr/traces.txt در دستگاه وجود دارد. در نسخه‌های جدیدتر سیستم‌عامل، چندین فایل /data/anr/anr_* وجود دارد. می‌توانید بااستفاده از Android Debug Bridge (adb) به‌عنوان ریشه، به ردیابی‌های ANR از دستگاه یا شبیه‌ساز دسترسی پیدا کنید:

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

می‌توانید بااستفاده از گزینه توسعه‌دهنده «گرفتن گزارش اشکال» در دستگاه یا فرمان adb bugreport در ماشین توسعه نرم‌افزار، گزارش اشکال را از دستگاه فیزیکی ضبط کنید. برای اطلاعات بیشتر، به ضبط و خواندن گزارش‌های اشکال مراجعه کنید.

برطرف کردن مشکلات

پس‌از شناسایی مشکل، می‌توانید از نکته‌های این بخش برای رفع مشکلات رایج استفاده کنید.

کد کند در رشته اصلی

مکان‌هایی را در کدتان شناسایی کنید که رشته اصلی برنامه برای بیش‌از ۵ ثانیه مشغول است. موارد استفاده مشکوک را در برنامه‌تان پیدا کنید و سعی کنید ANR را بازتولید کنید.

یک مشکل رایج، تکلیف طولانی‌مدت مستقیماً در یک ترکیب‌پذیر است:

@Composable
fun BadList(rawStrings: List<String>) {
    // Math or sorting inside the composable runs on EVERY recomposition pass!
    val heavilyProcessedList = rawStrings
        .filter { it.isNotBlank() }
        .map { it.uppercase().reversed() }
        .map { it.computationallyHeavyFunction() }
.sortedBy { it.length }
    LazyColumn { items(sortedList) { Text(it) } }
}

// Modern Compose-first fix
@Composable
fun GoodList(viewModel: MyViewModel = viewModel()) {
    val uiState by viewModel.uiState.collectAsStateWithLifecycle()

    // UI simply renders state; no heavy processing allowed here
    LazyColumn { items(uiState.sortedData) { Text(it) } }
}

ورودی/خروجی در رشته اصلی

اجرای عملیات ورودی/خروجی در رشته اصلی یکی از دلایل رایج عملکرد آهسته در رشته اصلی است که می‌تواند موجب بروز خطای ANR شود. در Compose، توسعه‌دهندگان اغلب به‌طور تصادفی خواندن دیسک را (مانند SharedPreferences یا فراخوانی‌های پایگاه داده) هنگام تلاش برای استخراج وضعیت اولیه فعال می‌کنند.

عملیات ورودی/خروجی طولانی‌مدت را دور از لایه واسط کاربر اجرا کنید. از withContext(Dispatchers.IO) در ViewModel استفاده کنید یا بهتر است از Repository در لایه داده استفاده کنید.

بن‌بست‌ها

بن‌بست زمانی رخ می‌دهد که رشته‌ای وارد حالت انتظار می‌شود زیرا منبع موردنیاز توسط رشته دیگری نگه‌داشته شده است، که آن رشته نیز منتظر منبعی است که توسط رشته اول نگه‌داشته شده است. اگر رشته اصلی برنامه در این وضعیت باشد، احتمالاً خطای ANR رخ می‌دهد.

بن‌بست‌ها پدیده‌ای هستند که در علوم رایانه به‌خوبی مطالعه شده‌اند و الگوریتم‌های جلوگیری از بن‌بست وجود دارد که می‌توانید برای اجتناب از بن‌بست از آن‌ها استفاده کنید.

برای اطلاعات بیشتر، به بن‌بست و الگوریتم‌های جلوگیری از بن‌بست در ویکی‌پدیا مراجعه کنید.

هنگام استفاده از Kotlin و Compose، می‌توانید قفل‌های اولیه را با Mutexهای غیرمسدودکننده کوروتین (Mutex.withLock) جایگزین کنید تا با تعلیق بافت اجرا به‌جای ثابت کردن رشته میانای کاربر، از مسدود شدن رشته جلوگیری کنید. برای مثال:

import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock

// Modern non-blocking concurrency state architecture
class SecureDataRepository {
    private val mutex = Mutex()

    suspend fun safeUIAccess() {
        // If locked, the main thread suspends seamlessly, preventing an ANR
        mutex.withLock {
            performSafeOperation()
        }
    }
}

گیرنده‌های رویداد ثبت‌شده کند

برنامه‌ها می‌توانند ازطریق گیرنده‌های رویداد ثبت‌شده به پیام‌های همه‌فرستی پاسخ دهند، مثلاً فعال یا غیرفعال کردن حالت هواپیما یا تغییر در وضعیت اتصال‌پذیری. وقتی برنامه برای پردازش پیام همه‌فرستی بیش‌ازحد طول بکشد، خطای «برنامه پاسخ نمی‌دهد» رخ می‌دهد.

خطای «برنامه پاسخ نمی‌دهد» در موارد زیر رخ می‌دهد:

  • گیرنده رویداد ثبت‌شده اجرای روش onReceive خود را در مدت زمان قابل‌توجهی به‌پایان نرسانده است.
  • گیرنده رویداد ثبت‌شده goAsync را فراخوانی می‌کند و نمی‌تواند finish را در شیء PendingResult فراخوانی کند.

برنامه شما باید فقط عملیات کوتاه را در روش onReceive BroadcastReceiver انجام دهد. بااین‌حال، اگر برنامه‌تان درنتیجه پیام همه‌فرستی به پردازش پیچیده‌تری نیاز دارد، باید تکلیف را به ViewModel (با بهره‌گیری از قدرت کوروتین‌ها، دامنه‌ها، و توزیع‌کننده‌های Kotlin) واگذار کنید اگر انتظار می‌رود تکلیف حداکثر چند ثانیه طول بکشد، هر نوع نگه‌دارنده حالت، یا به WorkManager برای تکالیفی که انتظار می‌رود بیشتر از چند ثانیه طول بکشد.

GameActivity

کتابخانه GameActivity در مطالعات موردی بازی‌ها و برنامه‌هایی که به زبان C یا ++C نوشته شده‌اند، ANR را کاهش داده است. اگر فعالیت بومی موجودتان را با GameActivity جایگزین کنید، می‌توانید مسدود شدن رشته میانای کاربر را کاهش دهید و از وقوع برخی ANRها جلوگیری کنید.

برای اطلاعات بیشتر درباره ANR، به حفظ پاسخ‌گویی برنامه مراجعه کنید. برای اطلاعات بیشتر درباره رشته‌ها، به عملکرد بهتر ازطریق رشته‌سازی مراجعه کنید.

منابع بیشتر

محتوا را مشاهده می‌کند