ANR(Application Not Responding)
ANR 메세지는 어느 동작에서 메인 스레드를 오랫동안 점유하고 있다는 의미이다.
다음 조건 중 하나가 발생하면 앱과 관련한 ANR이 트리거 된다.
- Activity가 foreground인 경우 input event(예 : 키 누름 또는 화면 터치 이벤트) 또는 Broadcast Receiver에 5초 이내 응답하지 않은 경우
- foreground에 activity가 없을 때 Broadcast Receiver가 상당한 시간 내에 실행을 완료하지 못한 경우
ANR Timeout
ANR 타임아웃을 프레임워크 소스에서 확인해보면 아래와 같다.
// How long we allow a receiver to run before giving up on it.
static final int BROADCAST_FG_TIMEOUT = 10*1000;
static final int BROADCAST_BG_TIMEOUT = 60*1000;
// How long we wait until we timeout on key dispatching.
static final int KEY_DISPATCHING_TIMEOUT = 5*1000;
/**
* ICS 이하 버전
*/
// How long we allow a receiver to run before giving up on it.
static final int BROADCAST_TIMEOUT = 10*1000;
// How long we wait for a service to finish executing.
static final int SERVICE_TIMEOUT = 20*1000;
// How long we wait until we timeout on key dispatching.
static final int KEY_DISPATCHING_TIMEOUT = 5*1000;
ICS이하 버전에서는 Broadcast Receiver 타임아웃이 10초였지만 이제는 1분(background)이면 타임아웃이 된다.
Broadcast Receiver를 foreground로 명시하는 방법은 sendBroadcast()에 전달되는 Intent에 Intent.FLAG_RECEIVER_FOREGROUND 플래그를 추가하는 것이다. ActivityManagerService에는 foreground/background 용도의 BroadcastQueue가 각각 있는데, 큐에 쌓인 순서에 관계없이 foreground 용도의 BroadcastQueue를 먼저 처리한다.
문제 감지 및 진단
Android vitals
앱을 이미 게시한 경우, ANR이 과도하게 발생하면 Play Console을 통해 알림을 보낸다.
Android vitals는 앱이 다음과 같을 때 ANR이 과도하다고 간주한다.
- 일일 세션의 0.47% 이상에서 한 번 이상의 ANR이 나타남.
- 일일 세션의 0.24% 이상에서 두 번 이상의 ANR이 나타남.
Android vitals 데이터를 수집하는 방법에 관한 자세한 내용은 Play Console 문서를 참조
StrictMode
StrictMode는 실수로 수행할 수 있는 일을 감지하고 주의를 기울여 수정할 수 있도록 하는 개발자 도구이다. StrictMode는 UI 작업이 수신되고 애니메이션이 발생하는 응용 프로그램의 메인 스레드에서 우발적인 디스크 또는 네트워크 액세스를 포착하는데 가장 일반적으로 사용된다. 애플리케이션의 메인 스레드를 반응형으로 유지하면 ANR 대화 상자가 사용자에게 표시되는 것도 방지할 수 있다.
public void onCreate() {
if (DEVELOPER_MODE) {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectNetwork() // or .detectAll() for all detectable problems
.penaltyLog()
.build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects()
.detectLeakedClosableObjects()
.penaltyLog()
.penaltyDeath()
.build());
}
super.onCreate();
}
앱을 개발하는 동안 메인 스레드에 실수로 인한 I/O 작업을 찾을 수 있지만 StrictMode가 찾은 모든 것을 수정해야 하는 것은 아니다.
Trace 파일 가져오기
Android에서는 ANR이 발생할 때 trace 정보를 저장한다. 이전 OS 버전에서는 기기에 하나의 /data/anr/traces.txt 파일이, 최신 OS 버전에는 여러 개의 /data/anr/anr_* 파일이 있다. 루트로 Android 디버그 브리지(adb)를 사용하여 액세스 할 수 있다.
adb root
adb shell ls /data/anr
adb pull /data/anr/<filename>
ANR 예방
Main Thread에서 실행되는 method는 최소한의 일을 하도록 해야 한다.
특히 onCreate(), onResume() 같은 핵심 생명주기에 속하는 method에서는 가능한 적은 일을 수행해야 한다.
다음은 안드로이드 측에서 권장하는 ANR의 예방 설계 방법이다.
- 시간 소모가 많은 작업은 스레드를 통해 처리하라.
- 사용자에게 프로그레스 바 등을 이용해 작업의 진행 과정을 안내해 기다리도록 한다.
References
- 노재춘, 안드로이드 프로그래밍 Next Step, 인사이트
- https://developer.android.com/topic/performance/vitals/anr