ANR(Application Not Responding)은 앱이 5초 이내에 입력에 응답하지 않을 때 나타나는 Android의 시스템 알림입니다. 글리치(UI 차단 없는 논리 오류) 및 랙(완전 중지 없는 느려짐)과 달리 ANR은 운영 체제에서 기록하는 중요한 오류입니다. Android는 “앱이 응답하지 않습니다” 대화상자를 표시하고 닫거나 기다리는 옵션을 제공합니다. Android Vitals Documentation에 따르면 ANR 비율이 0.5%를 초과하는 앱은 Google Play에서 낮은 평점을 받고 추천에서 숨겨질 수 있습니다. 진단에는 /data/anr/traces.txt 분석, StrictMode 사용 및 메인 스레드 프로파일링이 포함됩니다.
핵심 사항
ANR(Application Not Responding)은 앱이 입력에 응답하지 않을 때 활성화되는 Android의 사용자 보호 메커니즘입니다. 시스템은 이벤트 처리 시간을 추적합니다. BroadcastReceiver가 10초 이내에 onReceive를 완료하지 않거나, Service가 20초 이내에 onCreate에서 반환되지 않거나, ContentProvider가 15초 이내에 응답하지 않으면 Android가 ANR을 생성합니다.
ANR이 발생하면 Android는 모든 창 위에 시스템 대화상자를 표시합니다: “앱이 응답하지 않습니다. 닫거나 기다리시겠습니까?” 사용자는 앱을 닫거나 복구될 때까지 기다릴 수 있습니다. ANR이 자주 발생하면 사용자는 앱을 제거합니다. Google Play는 순위 알고리즘에서 ANR 비율(ANR이 발생한 세션의 백분율)을 고려합니다.
iOS에는 시스템 대화상자가 있는 ANR에 해당하는 기능이 없습니다. 대신 Apple은 Watchdog을 사용하여 종료 코드 0x8badf00d로 프로세스를 종료합니다. 사용자는 대화상자를 보지 못하며 앱이 홈 화면으로 그냥 닫힙니다. 이로 인해 Android의 ANR은 사용자에게 더 눈에 띄지만 시스템에 더 많은 진단 정보를 제공합니다.
ANR은 시스템이 네 가지 구성 요소 유형 중 하나에 대한 시간 초과를 추적할 때 발생합니다. 각 구성 요소에는 자체 시간 제한이 있습니다.
BroadcastReceiver는 메인 스레드에서 실행됩니다. onReceive가 동기 네트워크 요청, 데이터베이스에 긴 쓰기 작업을 시작하거나 잠금을 기다리면 10초 이내에 ANR이 발생합니다. 해결책: 백그라운드 처리를 위해 goAsync()와 WorkManager를 사용합니다. 일반적인 시나리오는 FCM에서 푸시 알림을 받고 Room에 동기적으로 저장하는 것입니다.
Service.onCreate와 Service.onStartCommand에는 20초 제한이 있습니다. 서비스가 메인 스레드에서 무거운 초기화(라이브러리 로드, 네트워크에서 구성 읽기)를 시작하면 ANR은 불가피합니다. 백그라운드에서 보장된 실행을 위해 IntentService(사용 중단) 또는 WorkManager를 사용합니다.
ContentProvider.onCreate는 Application.onCreate보다 먼저 실행되며 15초 제한이 있습니다. 제공자가 데이터베이스 마이그레이션, 사전 로드 또는 네트워크에서 SDK 초기화를 수행하면 앱 시작 시 ANR이 발생합니다. 해결책: 지연 초기화, 무거운 작업을 WorkManager로 오프로드합니다.
Android는 시스템 로그부터 전문 라이브러리까지 ANR 분석을 위한 여러 도구를 제공합니다.
각 ANR이 발생하면 Android는 모든 앱 스레드의 스택 덤프와 함께 /data/anr/traces.txt 파일을 저장합니다. “main” 스레드를 찾으십시오. 스택의 마지막 메서드가 원인을 나타냅니다. 일반적인 패턴: Thread.sleep(), InputStream.read(), BinderProxy.transact(). 장치에서 파일을 추출하려면 슈퍼유저 권한으로 adb를 사용합니다.
Firebase Crashlytics는 ANR을 자동으로 수집하고 추적과 함께 대시보드에 표시합니다. Android 11+의 경우 ANR 보고서에는 메인 스레드의 전체 스택이 포함됩니다. 통합하려면 종속성을 추가하고 Application.onCreate에서 FirebaseApp을 초기화해야 합니다.
Android Studio의 CPU Profiler를 사용하면 앱 추적을 기록하고 어떤 메서드가 CPU 시간을 소비하는지 확인할 수 있습니다. “Record with method traces”를 활성화하고 ANR을 유발하는 시나리오를 재현합니다. 타임라인은 멈춤 당시 메인 스레드에서 실행 중이던 메서드를 보여줍니다.
Android에서 ANR 수집을 위한 Firebase Crashlytics 통합 예:
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에 전달합니다.
모든 네트워크 요청, 데이터베이스 작업 및 파일 I/O는 Dispatchers.IO로 실행합니다. 메인 스레드는 UI만 업데이트해야 합니다. Activity가 소멸될 때 자동 코루틴 취소를 위해 viewModelScope를 사용합니다. 어떤 컨텍스트에서도 runBlocking()을 피하십시오. 현재 스레드를 동기적으로 차단합니다.
ContentProvider가 느린 초기화를 수행하는 경우 지연 로딩 메커니즘을 사용합니다: 즉시 데이터를 반환하는 제공자를 만들고 WorkManager를 통해 지연과 함께 무거운 초기화를 시작합니다. 이는 시스템이 지연에 가장 민감한 앱 시작 시 ANR을 방지합니다.
Android에서 goAsync와 함께 BroadcastReceiver를 올바르게 사용하는 예:
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에 대처하는 가장 좋은 방법은 도구와 아키텍처 결정을 통해 개발 중에 예방하는 것입니다.
활성화된 정책 detectNetwork() 및 detectDiskReads()/detectDiskWrites()와 함께 StrictMode는 개발 중 잠재적 ANR을 식별합니다. Debug 빌드에서는 penaltyDeath를 설정하십시오. 위반 시 즉시 충돌이 발생하며 개발자는 커밋 전에 문제를 확인할 수 있습니다.
Firebase Performance는 주요 작업의 실행 시간을 추적하고 어떤 시나리오가 ANR 임계값을 초과하는지 보여줍니다. 각 화면 및 네트워크 요청에 대한 사용자 지정 추적을 설정합니다. 실행 시간이 3초를 초과하면 최적화가 필요한 잠재적 ANR입니다.
느린 조건을 시뮬레이션합니다: iOS 또는 Android Emulator에서 Network Link Conditioner를 사용하여 네트워크 속도를 제한합니다. 느린 메모리 에뮬레이션을 통해 디스크 읽기를 느리게 합니다. ANR은 종종 이러한 조건에서 나타나며 빠른 개발자 장치에서는 보이지 않습니다.
자주 묻는 질문
Android는 메인 스레드의 이벤트 처리 시간을 명시적으로 추적하고 ANR 대화상자를 표시합니다. iOS는 Watchdog을 사용하여 10~20초 이상 멈추면 앱을 강제로 종료합니다. ANR은 여러 구성 요소(BroadcastReceiver, Service)에 엄격한 시간 제한이 있는 Android 아키텍처의 특징입니다.
Android 11+에서는 adb shell dumpsys dropbox --print data_app_anr을 통해 ANR 덤프를 얻을 수 있습니다. Android 10 이하에서는 루트 액세스 없이 /data/anr/traces.txt에 접근할 수 없습니다. Firebase Crashlytics를 사용하십시오. Android 11+에서 ANR 보고서를 자동으로 수집합니다.
Google Play는 ANR 비율을 0.5% 미만으로 권장합니다. 즉, 1000세션당 5개 이하의 ANR입니다. 비율이 1%를 초과하는 앱은 Google Play Console에서 경고를 받고 추천에서 숨겨질 수 있습니다. 이상적으로 ANR 비율은 0.1% 미만이어야 합니다.
코루틴 자체는 스레드를 차단하지 않습니다. 그러나 코루틴 내에서 메인 스레드에서 runBlocking을 실행하거나 코루틴이 Dispatchers.Main으로 시작되어 긴 CPU 작업을 수행하면 ANR이 발생합니다. I/O에는 Dispatchers.IO를, 계산에는 Dispatchers.Default를 사용합니다.
“Slow Network” 프로필이 있는 Android Emulator를 사용하거나 메인 스레드에서 Thread.sleep(6000)을 호출하는 테스트를 작성합니다. Debug를 통해 앱을 실행하고 5초 후 ANR 대화상자가 표시됩니다. logcat에 추적이 포함된 ANR 레코드가 표시되는지 확인합니다.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.