ANR

Android 앱의 UI 스레드가 너무 오랫동안 차단되면 'ANR(애플리케이션 응답 없음)' 오류가 트리거됩니다. Android vitals는 Google Play Console에서 ANR 측정항목을 추적하고 보고하여 여러 기기에서 앱의 안정성과 응답성을 모니터링하는 데 도움을 줍니다.

Android vitals 측정항목 및 기준점

Android vitals를 사용하면 앱의 ANR 발생률을 모니터링하고 개선할 수 있습니다. Android vitals는 다음과 같은 ANR 발생률을 측정합니다.

  • ANR 발생률: 모든 유형의 ANR을 경험한 일일 활성 사용자의 비율입니다.
  • 사용자 인식 ANR 발생률: 한 번 이상 사용자 인식 ANR을 경험한 일일 활성 사용자의 비율입니다. 현재 Input dispatching timed out 유형의 ANR만 사용자 인식 ANR로 간주됩니다.
  • 다중 ANR 발생률: ANR을 두 번 이상 경험한 일일 사용자의 비율입니다.

일일 활성 사용자는 한 기기에서 하루에 여러 세션에 걸쳐 앱을 사용하는 순 사용자입니다. 사용자가 하루에 두 대 이상의 기기에서 앱을 사용하는 경우 각 기기는 해당 날짜의 활성 사용자로 계산됩니다.

사용자 인식 ANR 발생률은 핵심 vitals에 속하는 요소로, Google Play에서 앱의 검색 가능 여부에 영향을 미칩니다. 사용자 인식 ANR 발생률에 집계되는 ANR은 항상 사용자가 앱을 사용할 때 발생하여 가장 큰 지장을 유발하므로 중요합니다.

Play에서는 이 측정항목에 대해 비정상적인 동작 기준 두 가지를 정의했습니다.

  • 전반적 비정상적인 동작 기준: 일일 활성 사용자의 0.47% 이상이 모든 기기 모델에서 사용자 인식 ANR을 경험합니다.
  • 기기별 비정상적인 동작 기준: 일일 사용자의 8% 이상이 단일 기기 모델에서 사용자 인식 ANR을 경험합니다.

앱이 전반적 비정상적인 동작 기준을 초과하면 모든 기기에서 검색 가능성이 낮아질 수 있습니다. 앱이 일부 기기에서 기기별 비정상적인 동작 기준을 초과하면 해당 기기에서 검색 가능성이 낮아질 수 있으며 스토어 등록정보에 경고가 표시될 수도 있습니다.

앱에서 ANR이 과도하게 발생하면 Android vitals에서 Play Console을 통해 알림을 보낼 수 있습니다.

Google Play에서 Android vitals 데이터를 수집하는 방법에 관한 자세한 내용은 Play Console 문서를 참고하세요.

문제 감지

ANR이 발생할 때 앱이 포그라운드에 있으면 그림 1과 같이 시스템에서 사용자에게 대화상자를 표시합니다. 사용자가 ANR 대화상자에서 앱을 강제 종료할 수 있습니다.

사용자에게 표시되는 ANR 대화상자
그림 1. 사용자에게 표시되는 ANR 대화상자

ANR은 UI 업데이트를 담당하는 앱의 기본 스레드가 사용자 입력 이벤트 또는 그리기를 처리하지 못하여 사용자 불만을 초래하므로 문제가 됩니다. 앱의 기본 스레드에 관한 자세한 내용은 프로세스 및 스레드 개요를 참고하세요.

다음 조건 중 하나가 발생하면 앱과 관련한 ANR이 트리거됩니다.

  • 입력 전달 타임아웃: 앱이 입력 이벤트 (예: 키 누름 또는 화면 터치)에 5초 이내에 응답하지 않은 경우
  • 서비스 실행: 앱에서 선언한 서비스가 몇 초 이내에 Service.onCreate 및 Service.onStartCommand 또는 Service.onBind 실행을 완료할 수 없는 경우
  • Service.startForeground가 호출되지 않음: 앱이 Context.startForegroundService를 사용하여 포그라운드에서 새 서비스를 시작했지만 서비스가 5초 내에 startForeground를 호출하지 않은 경우
  • 인텐트 브로드캐스트: BroadcastReceiver가 설정된 시간 내에 실행을 완료하지 못한 경우. 앱에 포그라운드 활동이 있는 경우 이 제한 시간은 10초입니다.
  • JobScheduler 상호작용: JobService가 몇 초 이내에 JobService.onStartJob 또는 JobService.onStopJob에서 반환되지 않거나 사용자 시작 작업이 시작되고 JobService.onStartJob이 호출된 후 몇 초 이내에 앱이 JobService.setNotification을 호출하지 않는 경우. Android 13 및 이전 버전을 타겟팅하는 앱의 경우 ANR이 음소거되고 앱에 보고되지 않습니다. Android 14 및 이후 버전을 타겟팅하는 앱의 경우 ANR이 명시적이고 앱에 보고됩니다.

Android vitals 외에도 다른 도구를 사용하여 필드에서 ANR을 감지할 수 있지만 서드 파티 도구는 Android 10 (API 수준 29) 이하에서 ANR을 보고할 수 없습니다.

ANR 진단

ANR을 진단할 때 다음과 같은 일반 패턴을 찾아야 합니다.

  • 앱이 기본 스레드에서 I/O와 관련된 느린 작업을 실행 중입니다.
  • 앱이 기본 스레드에서 긴 계산을 실행 중입니다.
  • 기본 스레드에서 다른 프로세스에 관한 동기 바인더 호출을 실행 중이고 다른 프로세스가 반환하는 데 오랜 시간이 걸립니다.
  • 다른 스레드에서 발생하는 긴 작업을 위해 동기화된 블록을 대기하는 동안 기본 스레드가 차단되었습니다.
  • 기본 스레드가 프로세스에서 또는 바인더 호출을 통해 다른 스레드와 교착 상태에 있습니다. 기본 스레드가 긴 작업이 완료될 때까지 대기하는 것만이 아니라 교착 상태에 있습니다.

다음 기법을 통해 ANR의 원인을 확인할 수 있습니다.

HealthStats

HealthStats는 총 사용자 및 시스템 시간, CPU 시간, 네트워크, 무선 통계, 화면 켜짐/꺼짐 시간, 깨우기 알람을 캡처하여 애플리케이션의 상태에 관한 측정항목을 제공합니다. 이렇게 하면 전체 CPU 사용량과 배터리 소모를 측정하는 데 도움이 됩니다.

디버그

Debug는 앱의 버벅거림 및 지연을 식별하는 추적 및 할당 수를 포함하여 개발 중에 Android 애플리케이션을 검사하는 데 도움이 됩니다. Debug를 사용하여 런타임 및 네이티브 메모리 카운터, 특정 프로세스의 메모리 사용량을 식별하는 데 도움이 될 수 있는 메모리 측정항목을 얻을 수도 있습니다.

ApplicationExitInfo

ApplicationExitInfo는 Android 11 (API 수준 30) 이상에서 사용할 수 있으며 애플리케이션 종료 이유에 관한 정보를 제공합니다. 여기에는 ANR, 메모리 부족, 앱 비정상 종료, 과도한 CPU 사용, 사용자 중단, 시스템 중단, 런타임 권한 변경이 포함됩니다.

엄격 모드

StrictMode를 사용하면 앱을 개발하는 동안 기본 스레드에서 실수로 인한 I/O 작업을 찾을 수 있습니다. 애플리케이션 또는 활동 수준에서 StrictMode를 사용할 수 있습니다.

백그라운드 ANR 대화상자 사용 설정

Android는 기기의 개발자 옵션에서 모든 ANR 표시가 사용 설정된 경우에만 브로드캐스트 메시지를 처리하는 데 너무 오래 걸리는 앱에 관한 ANR 대화상자를 표시합니다. 따라서 앱에 성능 문제가 발생하고 있더라도 백그라운드 ANR 대화상자가 항상 사용자에게 표시되지는 않습니다.

CPU 및 시스템 트레이스 프로파일링

Android 스튜디오 CPU 프로파일러를 사용하여 게임이 실행되는 동안 시스템 트레이스와 CPU 활동을 기록합니다. 게임 엔진의 렌더링, 오디오, 작업자 스레드와 함께 기본 Activity 스레드를 검사하면 ANR이 트리거되기 전에 긴 프레임, 동기식 애셋 로드, 스레드 경합을 발견하는 데 도움이 됩니다.

트레이스 파일 가져오기

Android에서는 ANR이 발생할 때 트레이스 정보를 저장합니다. 이전 Android 버전에는 기기에 하나의 /data/anr/traces.txt 파일이 있습니다. 상위 Android 버전에는 여러 개의 /data/anr/anr_* 파일이 있습니다. 루트로 Android 디버그 브리지 (adb)를 사용하여 기기 또는 에뮬레이터에서 ANR 트레이스에 액세스할 수 있습니다.

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

기기에서 버그 신고 개발자 옵션을 사용하거나 개발 머신에서 adb bugreport 명령어를 사용하여 실제 기기에서 버그 신고를 캡처할 수 있습니다. 자세한 내용은 버그 신고 캡처 및 읽기를 참고하세요.

문제 해결

문제를 식별한 후 이 섹션의 도움말을 사용하여 자주 발생하는 문제를 해결할 수 있습니다.

기본 스레드의 느린 코드

앱의 기본 스레드를 5초 이상 사용 중인 코드의 위치를 확인하세요. 게임에서 의심스러운 사용 사례를 찾아 ANR을 재현해 보세요.

게임에서 기본 스레드 정지는 일반적으로 동기 애셋 압축 해제, 셰이더 컴파일 또는 과도한 게임 상태 초기화가 Android 기본 Activity 스레드 (예: onCreate, onResume 또는 UI 스레드에서 호출된 JNI 콜백 내부)에서 실행될 때 발생합니다. 게임 루프 실행, 애셋 처리, 장면 초기화를 전용 엔진, 렌더링 또는 백그라운드 작업자 스레드로 이동하여 기본 스레드가 입력 및 수명 주기 이벤트를 자유롭게 처리할 수 있도록 합니다.

기본 스레드의 I/O

기본 스레드에서 I/O 작업을 실행하는 것은 기본 스레드의 작업이 느려지는 일반적인 원인이며 이로 인해 ANR이 발생할 수 있습니다.

모든 애셋 번들 읽기, 저장된 게임 직렬화, SharedPreferences 액세스, 네트워크 요청을 기본 Activity 스레드에서 백그라운드 작업자 스레드로 이동하여 저장소 또는 네트워크 지연 시간이 입력 전달을 차단하지 않도록 합니다.

교착 상태

한 스레드에서 필요한 리소스를 다른 스레드가 보유하고 있고, 다른 스레드 역시 첫 번째 스레드가 보유 중인 리소스를 대기하고 있어 스레드가 대기 상태가 되는 경우 교착 상태가 발생합니다. 앱의 기본 스레드가 이 상황이면 ANR이 발생할 가능성이 큽니다.

게임에서는 Android 기본 Activity 스레드 (수명 주기, 표시 경로 또는 입력 콜백 처리)와 네이티브 C 또는 C++ 렌더링, 로직 또는 오디오 스레드 간에 교착 상태와 잠금 경합이 자주 발생합니다. 네이티브 엔진 잠금의 기본 스레드에서 동기식 대기를 피하고 JNI와 엔진 스레드 간에 통신할 때 일관된 잠금 순서 또는 잠금 없는 메시지 큐를 사용합니다.

교착 상태는 컴퓨터 공학에서 철저히 연구된 현상으로, 교착 상태 방지 알고리즘을 사용하여 교착 상태를 방지할 수 있습니다.

자세한 내용은 위키백과의 교착 상태 및 교착 상태 방지 알고리즘을 참고하세요.

느린 broadcast receiver

앱은 broadcast receiver를 사용하여 비행기 모드 사용 설정 또는 중지나 연결 상태 변경과 같은 브로드캐스트 메시지에 응답할 수 있습니다. 앱이 브로드캐스트 메시지를 처리하는 데 너무 오래 걸리면 ANR이 발생합니다.

다음과 같은 경우에 ANR이 발생합니다.

  • broadcast receiver가 상당한 시간 내에 onReceive 메서드 실행을 완료하지 못했습니다.
  • broadcast receiver가 goAsync를 호출하지만 PendingResult 객체에서 finish를 호출하지 못합니다.

앱은 BroadcastReceiver의 onReceive 메서드에서 짧은 작업만 실행해야 합니다. 하지만 브로드캐스트 메시지로 인해 앱에 더 복잡한 처리가 필요하다면 작업이 최대 몇 초 정도 걸릴 것으로 예상되는 경우 작업을 백그라운드 작업자 스레드로 이전해야 하고, 작업이 몇 초 이상 걸릴 것으로 예상되는 경우 WorkManager로 이전해야 합니다.

GameActivity

C 또는 C++로 작성된 게임과 앱의 우수사례를 보면 GameActivity 사용으로 인해 ANR이 줄었습니다. 기존 네이티브 액티비티를 GameActivity로 교체한다면 UI 스레드 차단을 줄이고 ANR이 발생하는 것을 어느 정도 막을 수 있습니다.

Unity로 게임을 빌드하는 경우 Unity 게임의 ANR 디버그, 일반적인 Unity 게임 ANR, Unity 게임의 Android 비정상 종료 및 ANR 기호화를 참고하세요.

ANR에 관한 자세한 내용은 앱 응답성 유지를 참고하세요. 스레드에 관한 자세한 내용은 스레딩을 통한 성능 개선을 참고하세요.

  • 참고: JavaScript가 사용 중지되어 있으면 링크 텍스트가 표시됩니다.
  • 불필요한 wakeup