안드로이드 시스템·Compose 용어 사전
성능Application Not Responding · StrictMode

ANR

메인 스레드가 막혀 시스템이 앱을 응답 없음으로 판정하는 것. 입력 5초가 기준.

메인 스레드가 막혀 시스템이 응답 없음으로 판정하는 것.

  • 입력 이벤트 처리 — 5초
  • 포그라운드 서비스 시작 — 5초 (startForeground 미호출)
  • BroadcastReceiver — 10초 (포그라운드) / 60초 (백그라운드)
  • ContentProvider — 10초

메인 스레드가 하는 일

  • 화면 그리기 (16.6ms 마다 한 프레임 — 60Hz 기준)
  • 터치 이벤트 처리
  • 생명주기 콜백

여기서 100ms 를 쓰면 6프레임이 날아간다 (눈에 보인다) 5초를 쓰면 ANR 다

전형적인 원인

  • 메인에서 네트워크 · 파일 · DB 접근

  • 메인에서 큰 JSON 파싱 · 이미지 디코딩 · 암호화

  • 락 경합 (메인이 백그라운드 스레드가 쥔 락을 기다린다)

  • 데드락

  • Binder 호출이 늦는 경우 (다른 앱/시스템 프로세스 대기)

  • "IO 를 안 했는데 ANR" 이면 락 경합을 의심한다

StrictMode로 개발 중에 잡는다

if (BuildConfig.DEBUG) {
    StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.Builder()
        .detectDiskReads().detectDiskWrites().detectNetwork()
        .penaltyLog()
        .build())
    StrictMode.setVmPolicy(StrictMode.VmPolicy.Builder()
        .detectLeakedClosableObjects().detectActivityLeaks()
        .penaltyLog().build())
}

메인 스레드에서 디스크·네트워크를 건드리면 즉시 로그를 남긴다 penaltyDeath() 로 하면 크래시시켜 강제로 고치게 만들 수도 있다

원인을 찾는 법

  • /data/anr/traces.txt — 모든 스레드의 스택이 찍힌다
    • main 스레드가 무엇을 하고 있었는지가 첫 번째 단서
  • adb bugreport
  • Play Console → Android vitals → ANR 비율
    • "ANR 발생률 0.47% 초과" 면 스토어 노출이 불이익을 받는다

SharedPreferences가 흔한 함정

prefs.edit().putString(k, v).apply()    // 비동기. 하지만 완전히 공짜는 아니다
prefs.edit().putString(k, v).commit()   // 동기 디스크 쓰기 — 메인에서 부르면 위험

apply() 도 첫 getSharedPreferences 로드는 동기다 그리고 Activity 의 onPause/onStop 은 apply() 대기열이 비워질 때까지 기다린다

  • 큰 prefs 가 화면 전환을 막을 수 있다

  • DataStore 로 옮기면 이 문제가 사라진다

면접 함정

  • "5초 안에만 끝나면 된다" → 16ms를 넘기면 이미 프레임이 밀린다. ANR은 최악의 경계일 뿐이다.
  • "코루틴을 쓰면 안전하다"Dispatchers.Main에서 블로킹 코드를 돌리면 똑같다.

락 경합으로 생기는 ANR

// ❌ 메인이 IO 스레드가 쥔 락을 기다린다
synchronized(lock) { readFromDisk() }        // IO 스레드
synchronized(lock) { getCachedValue() }      // 메인 스레드 → 여기서 멈춘다

traces.txt 에서 이렇게 보인다

  • "main" ... Blocked on lock held by thread 21
  • 21번 스레드가 무엇을 하고 있었는지를 함께 본다

프레임 예산

  • 60Hz — → 16.6ms
  • 90Hz — → 11.1ms
  • 120Hz → 8.3ms

이 안에 '측정 + 배치 + 그리기' 를 끝내야 한다 넘으면 그 프레임이 버려지고 화면이 끊긴다 (잰크)

고주사율 기기가 흔해지며 예산이 절반으로 줄었다

무엇이 프레임을 먹는가

  • 리컴포지션 폭주 (불안정한 파라미터)

  • 목록 항목에서 이미지 디코딩

  • 메인에서의 정렬·필터링

  • 과도한 오버드로 (겹친 배경을 여러 번 칠한다)

  • 레이아웃 중첩이 깊다

  • Perfetto 로 프레임 단위 추적을 뜨면 어느 단계가 오래 걸렸는지 정확히 보인다

백그라운드 ANR도 있다

  • 포그라운드 서비스가 5초 안에 startForeground 를 안 부른다

  • BroadcastReceiver.onReceive 가 제한을 넘는다

  • ContentProvider 가 10초를 넘는다

  • 화면이 없으니 사용자는 모르지만 vitals 에는 집계된다

함께 보면 좋은 용어

노트에서 맥락과 함께 보기 — 메모리·ANR·성능 — 누수·프리징·렉