ANR 진단 및 해결

Android 앱의 UI 스레드가 너무 오랫동안 차단되면 시스템에서 '애플리케이션 응답 없음' (ANR) 오류를 전송합니다. 이 페이지에서는 다양한 유형의 ANR, 진단 방법, 해결 방법을 설명합니다. 나열된 모든 기본 시간 제한 시간 범위는 AOSP 및 Pixel 기기용입니다. 이 시간은 OEM마다 다를 수 있습니다.

ANR의 원인을 판단할 때 시스템 문제와 앱 문제를 구분하면 도움이 됩니다.

시스템 상태가 좋지 않으면 다음과 같은 문제로 인해 ANR이 발생할 수 있습니다.

  • 시스템 서버에 일시적인 문제가 있으면 일반적으로 빠른 바인더 호출이 느려집니다.
  • 시스템 서버 문제 및 높은 기기 부하로 인해 앱 스레드가 예약되지 않습니다.

가능한 경우 시스템 문제와 앱 문제를 구분하는 좋은 방법은 Perfetto 트레이스를 사용하는 것입니다.

  • Perfetto에서 스레드 상태 트랙을 살펴보고 앱의 기본 스레드가 실행 중인지 또는 실행 가능한지 확인하여 앱의 기본 스레드가 예약되었는지 확인합니다.
  • system_server 스레드에 잠금 경합과 같은 문제가 있는지 확인합니다.
  • 느린 바인더 호출의 경우 응답 스레드(있는 경우)를 살펴보고 속도가 느린 이유를 확인합니다.

입력 전달 시간 초과

입력 전달 ANR은 앱의 기본 스레드가 스와이프 또는 키 누름과 같은 입력 이벤트에 제때 응답하지 않을 때 발생합니다. 입력 전달 시간 제한이 발생하면 앱이 포그라운드에 있으므로 거의 항상 사용자에게 표시되고 완화하는 것이 매우 중요합니다.

기본 제한 시간: 5초

입력 디스패치 ANR은 일반적으로 기본 스레드의 문제로 인해 발생합니다. 잠금을 획득하기 위해 대기 중인 기본 스레드가 차단된 경우 홀더 스레드도 관련될 수 있습니다.

입력 전달 ANR을 방지하려면 다음 권장사항을 따르세요.

  • 기본 스레드에서 차단 또는 장기 실행 작업을 실행하지 않습니다. StrictMode를 사용하여 기본 스레드에서 실수로 인한 활동을 포착하는 것이 좋습니다.
  • 기본 스레드와 다른 스레드 간의 잠금 경합을 최소화합니다.
  • 브로드캐스트를 처리하거나 서비스를 실행할 때와 같이 기본 스레드에서 UI가 아닌 작업을 최소화합니다.

일반적인 원인

다음은 입력 전달 ANR의 일반적인 원인과 해결 방법입니다.

원인 결과 추천 수정사항
느린 바인더 호출 기본 스레드에서 긴 동기 바인더를 호출합니다. API를 소유한 경우 호출을 기본 스레드 외부로 이동하거나 호출을 최적화해 봅니다.
여러 차례의 연속 바인더 호출 기본 스레드에서 동기식 바인더를 여러 번 연속으로 호출합니다. 단단한 루프에서 바인더 호출을 실행하지 마세요.
차단 I/O 기본 스레드가 데이터베이스 또는 네트워크 액세스와 같은 차단 I/O 호출을 실행합니다. 모든 차단 IO를 기본 스레드 외부로 이동합니다.
잠금 경합 잠금을 획득하기 위한 대기 중인 기본 스레드가 차단되었습니다. 기본 스레드와 다른 스레드 간의 잠금 경합을 줄입니다. 다른 스레드에서 느린 코드를 최적화합니다.
고가의 프레임 단일 프레임에서 너무 많이 렌더링되어 심각한 버벅거림이 발생합니다. 프레임 렌더링 작업을 줄입니다. n2 알고리즘을 사용하지 않습니다. 스크롤 또는 페이징과 같은 작업에 효율적인 구성요소를 사용합니다(예: Jetpack Paging 라이브러리).
다른 구성요소에 의해 차단됨 broadcast receiver와 같은 다른 구성요소가 실행 중이며 기본 스레드를 차단하고 있습니다. UI가 아닌 작업을 기본 스레드 외부로 최대한 이동합니다. 다른 스레드에서 broadcast receiver를 실행합니다.
GPU 중단 GPU 중단은 렌더링이 차단되어 입력 디스패치 ANR을 유발하는 시스템 또는 하드웨어 문제입니다. 안타깝게도 일반적으로 앱 측에 수정사항은 없습니다. 가능한 경우 하드웨어팀에 문의하여 문제를 해결하세요.

디버깅 방법

Google Play Console 또는 Firebase Crashlytics에서 ANR 클러스터 서명을 확인하여 디버깅을 시작합니다. 일반적으로 클러스터에는 ANR을 일으킨 것으로 의심되는 상위 프레임이 포함되어 있습니다.

다음 플로우 차트는 입력 시간 제한 전달 ANR의 원인을 확인하는 방법을 보여줍니다.

그림 1. 입력 디스패치 ANR을 디버그하는 방법

Play vitals는 이러한 일반적인 ANR 원인을 감지하고 디버그하는 데 도움을 줄 수 있습니다. 예를 들어 vitals에서 잠금 경합으로 인해 ANR이 발생했음을 감지하면 ANR 통계 섹션에 문제를 요약하고 권장되는 해결 방법을 표시할 수 있습니다.

그림 2. Play vitals ANR 감지

포커스가 설정된 창 없음

터치와 같은 이벤트는 히트 테스트를 기반으로 관련 창으로 직접 전송되지만 키와 같은 이벤트에는 타겟이 필요합니다. 이 타겟을 포커스가 있는 창이라고 합니다. 디스플레이당 포커스가 맞춰진 창은 하나만 있으며 일반적으로 사용자가 현재 상호작용하고 있는 창입니다. 포커스가 맞춰진 창을 찾을 수 없으면 입력 시 no-focus-window ANR이 발생합니다. 포커스가 없는 기간 ANR은 입력 디스패치 ANR의 한 유형입니다.

기본 제한 시간: 5초

일반적인 원인

포커스가 없는 창 ANR은 일반적으로 다음 문제 중 하나로 인해 발생합니다.

  • 앱이 너무 많은 작업을 하고 있고 너무 느려 첫 번째 프레임을 그릴 수 없습니다.
  • 기본 창에 포커스를 둘 수 없습니다. 창이 FLAG_NOT_FOCUSABLE로 신고되면 사용자는 키 또는 버튼 이벤트를 보낼 수 없습니다.

Kotlin

override fun onCreate(savedInstanceState: Bundle) {
  super.onCreate(savedInstanceState)
  setContentView(R.layout.activity_main)
  window.addFlags(WindowManager.LayoutParams.FLAG_FLAG_NOT_FOCUSABLE)
}

Java

@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  getWindow().addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
}

broadcast receiver 시간 초과

broadcast receiver ANR은 broadcast receiver가 적시에 브로드캐스트를 처리하지 않을 때 발생합니다. 동기 수신기 또는 goAync()를 호출하지 않는 수신기의 경우 시간 초과는 onReceive()가 제때 완료되지 않았음을 의미합니다. 비동기 수신기 또는 goAsync()를 호출하는 수신기의 경우 제한 시간은 PendingResult.finish()가 제때 호출되지 않았음을 의미합니다.

broadcast receiver ANR은 주로 다음 스레드에서 발생합니다.

  • 기본 스레드(앱 시작 속도가 느린 경우)
  • broadcast receiver를 실행하는 스레드(문제가 느린 onReceive() 코드인 경우)
  • 브로드캐스트 작업자 스레드(문제가 느린 goAsync() 브로드캐스트 코드인 경우)

broadcast receiver ANR을 방지하려면 다음 권장사항을 따르세요.

  • 앱이 브로드캐스트를 처리하기 시작하면 ANR 제한 시간에 집계되므로 앱 시작이 빠른지 확인합니다.
  • goAsync()를 사용하는 경우 PendingResult.finish()가 빠르게 호출되는지 확인합니다. 이 경우 동기 broadcast receiver와 동일한 ANR 시간 제한이 적용됩니다.
  • goAsync()를 사용하는 경우 작업자 스레드가 다른 장기 실행 작업 또는 차단 작업과 공유되지 않아야 합니다.
  • 기본 스레드에서 실행 중인 UI 코드가 차단되지 않도록 registerReceiver()을 사용하여 기본 스레드가 아닌 스레드에서 broadcast receiver를 실행하는 것이 좋습니다.

제한 시간

브로드캐스트 수신 제한 시간은 포그라운드 인텐트 플래그 설정 여부와 플랫폼 버전에 따라 다릅니다.

인텐트 유형 Android 13 및 이전 버전 Android 14 및 이후 버전

포그라운드 우선순위 인텐트

(FLAG_RECEIVER_FOREGROUND개 설정됨)

10초

10-20초(프로세스의 CPU 부족 여부에 따라 다름)

백그라운드 우선순위 인텐트

(FLAG_RECEIVER_FOREGROUND 설정되지 않음)

60초

60-120초(프로세스의 CPU 부족 여부에 따라 다름)

FLAG_RECEIVER_FOREGROUND 플래그가 설정되어 있는지 확인하려면 ANR 제목에서 'flg='를 찾고 0x10000000가 있는지 확인합니다. 이 비트가 설정되면 인텐트에 FLAG_RECEIVER_FOREGROUND가 설정되므로 제한 시간이 더 짧습니다.

짧은 브로드캐스트 시간 제한 (10~20초)이 있는 ANR 주체의 예:

Broadcast of Intent { act=android.inent.action.SCREEN_ON flg=0x50200010 }

브로드캐스트 시간 제한이 긴 ANR 주체 (60~120초)의 예:

Broadcast of Intent { act=android.intent.action.TIME_SET flg=0x25200010 }

브로드캐스트 시간 측정 방법

브로드캐스트 지속 시간 측정은 브로드캐스트가 system_server에서 앱으로 전달될 때 시작되고 앱이 브로드캐스트 처리를 완료하면 완료됩니다. 앱 프로세스가 아직 실행되고 있지 않다면 ANR 시간 제한 내에 콜드 스타트도 실행해야 합니다. 따라서 앱 시작 속도가 느리면 broadcast receiver ANR이 발생할 수 있습니다.

다음 그림은 broadcast receiver ANR 타임라인이 특정 앱 프로세스와 일치하는지 보여줍니다.

그림 3. broadcast receiver ANR 타임라인

ANR 시간 제한 측정은 broadcast receiver가 브로드캐스트 처리를 완료하면 종료됩니다. 정확한 시점은 동기 수신자인지 비동기 수신기인지에 따라 다릅니다.

  • 동기 수신기의 경우 onReceive()가 반환되면 측정이 중지됩니다.
  • 비동기 수신기의 경우 PendingResult.finish()가 호출될 때 측정이 중지됩니다.
그림 4. 동기 및 비동기 수신기의 ANR 시간 제한 측정 엔드포인트입니다.

일반적인 원인

다음은 broadcast receiver ANR의 일반적인 원인과 해결 방법입니다.

원인 적용 대상 발생한 문제 추천 수정사항
느린 앱 시작 모든 수신기 앱이 콜드 스타트를 시작하는 데 너무 오래 걸렸습니다. 느린 앱 시작을 최적화합니다.
onReceive()개 예약되지 않음 모든 수신기 broadcast receiver 스레드가 다른 작업을 수행하느라 onReceive() 메서드를 시작할 수 없습니다. 수신자 스레드에서 장기 실행 작업을 실행하거나 수신기를 전용 스레드로 이동하지 마세요.
느린 onReceive() 모든 수신기, 주로 동기식 수신기 onReceive() 메서드가 시작되었지만 차단되었거나 느려서 시간 내에 완료되지 않았습니다. 느린 수신기 코드를 최적화합니다.
비동기 수신기 작업이 예약되지 않음 수신기 goAsync() onReceive() 메서드가 차단된 작업자 스레드 풀에서 작업을 실행하려고 했으므로 작업이 시작되지 않았습니다. 느린 호출이나 차단 호출을 최적화하거나 브로드캐스트 작업자와 기타 장기 실행 작업에 서로 다른 스레드를 사용하세요.
작업자가 느리거나 차단됨 수신기 goAsync() 브로드캐스트를 처리하는 동안 작업자 스레드 풀 어딘가에 차단되거나 느린 작업이 발생했습니다. 따라서 PendingResult.finish는 제시간에 호출되지 않았습니다. 느린 async 수신기 코드를 최적화합니다.
PendingResult.finish님에게 전화를 걸지 않음 수신기 goAsync() 코드 경로에서 finish() 호출이 누락되었습니다. finish()가 항상 호출되도록 합니다.

디버깅 방법

클러스터 서명 및 ANR 보고서에 따라 수신기가 실행되는 스레드를 찾은 다음 누락되거나 느리게 실행되는 특정 코드를 찾을 수 있습니다.

다음 순서도는 broadcast receiver ANR의 원인을 확인하는 방법을 보여줍니다.

그림 5. broadcast receiver ANR을 디버그하는 방법

수신기 코드 찾기

Google Play Console은 ANR 서명에 수신기 클래스와 브로드캐스트 인텐트를 표시합니다. 다음을 확인하세요.

  • cmp=<receiver class>
  • act=<broadcast_intent>

다음은 broadcast receiver ANR 서명의 예입니다.

com.example.app.MyClass.myMethod
Broadcast of Intent { act=android.accounts.LOGIN_ACCOUNTS_CHANGED
cmp=com.example.app/com.example.app.MyAccountReceiver }

onReceive() 메서드를 실행하는 스레드 찾기

Context.registerReceiver를 사용하여 커스텀 핸들러를 지정하고 있다면 이 핸들러가 이 핸들러를 실행하는 스레드입니다. 그 외의 경우에는 기본 스레드입니다.

예: 비동기 수신기 작업이 예약되지 않음

이 섹션에서는 broadcast receiver ANR을 디버그하는 방법의 예를 살펴봅니다.

ANR 서명이 다음과 같다고 가정해 보겠습니다.

com.example.app.MyClass.myMethod
Broadcast of Intent {
act=android.accounts.LOG_ACCOUNTS_CHANGED cmp=com.example.app/com.example.app.MyReceiver }

서명에 따르면 브로드캐스트 인텐트는 android.accounts.LOG_ACCOUNTS_CHANGED이고 수신기 클래스는 com.example.app.MyReceiver인 것 같습니다.

수신자 코드에서 'BG Thread [0,1,2,3]' 스레드 풀이 이 브로드캐스트를 처리하기 위한 기본 작업을 실행하는지 확인할 수 있습니다. 스택 덤프를 살펴보면 4개의 백그라운드 (BG) 스레드 모두 동일한 패턴, 즉 차단 호출 getDataSync를 실행하는 것을 확인할 수 있습니다. 모든 BG 스레드가 사용 중이었기 때문에 브로드캐스트를 제시간에 처리할 수 없어 ANR이 발생했습니다.

BG Thread #0 (tid=26) Waiting

at jdk.internal.misc.Unsafe.park(Native method:0)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:211)
at com.google.common.util.concurrent.AbstractFuture.get(AbstractFuture:563)
at com.google.common.util.concurrent.ForwardingFuture.get(ForwardingFuture:68)
at com.example.app.getDataSync(<MyClass>:152)

...

at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:644)
at com.google.android.libraries.concurrent.AndroidExecutorsModule.lambda$withStrictMode$5(AndroidExecutorsModule:451)
at com.google.android.libraries.concurrent.AndroidExecutorsModule$$ExternalSyntheticLambda8.run(AndroidExecutorsModule:1)
at java.lang.Thread.run(Thread.java:1012)
at com.google.android.libraries.concurrent.ManagedPriorityThread.run(ManagedPriorityThread:34)

There are several approaches to fix the issue:

  • Find out why getDataSync is slow and optimize.
  • Don't run getDataSync on all four BG threads.
  • More generally, ensure that the BG thread pool isn't saturated with long-running operations.
  • Use a dedicated thread pool for goAsync worker tasks.
  • Use an unbounded thread pool instead of the bounded BG thread pool

Example: slow app startup

A slow app startup can cause several types of ANRs, especially broadcast receiver and execute service ANRs. The cause of an ANR is likely slow app startup if you see ActivityThread.handleBindApplication in the main thread stacks.

Execute service timeout

An execute service ANR happens when the app's main thread doesn't start a service in time. Specifically, a service doesn't finish executing onCreate() and onStartCommand() or onBind() within the timeout period.

Default timeout period: 20 seconds for foreground service; 200 seconds for background service. The ANR timeout period includes the app cold start, if necessary, and calls to onCreate(), onBind(), or onStartCommand().

To avoid execute service ANRs, follow these general best practices:

  • Make sure that app startup is fast, since it's counted in the ANR timeout if the app is started to run the service component.
  • Make sure that the service's onCreate(), onStartCommand(), and onBind() methods are fast.
  • Avoid running any slow or blocking operations on the main thread from other components; these operations can prevent a service from starting quickly.

Common causes

The following table lists common causes of execute service ANRs and suggested fixes.

Cause What Suggested fix
Slow app startup The app takes too long to perform a cold start. Optimize slow app start.
Slow onCreate(), onStartCommand(), or onBind() The service component's onCreate(), onStartCommand(), or onBind() method takes too long to execute on the main thread. Optimize slow code. Move slow operations off the critical path where possible.
Not scheduled (main thread blocked before onStart()) The app's main thread is blocked by another component before the service can be started. Move other component's work off the main thread. Optimize other component's blocking code.

How to debug

From the cluster signature and ANR report in Google Play Console or Firebase Crashlytics, you can often determine the cause of the ANR based on what the main thread is doing.

The following flow chart describes how to debug an execute service ANR.

Figure 6. How to debug an execute service ANR.

If you've determined that the execute service ANR is actionable, follow these steps to help resolve the issue:

  1. Find the service component class in the ANR signature. In Google Play Console, the service component class is shown in the ANR signature. In the following example ANR details, it's com.example.app/MyService.

    com.google.common.util.concurrent.Uninterruptibles.awaitUninterruptibly
    Executing service com.example.app/com.example.app.MyService
    
  2. Determine whether the slow or block operation is part of app startup, the service component, or elsewhere by checking for the following important function call(s) in the main threads.

    Function call(s) in main thread stacks What it means
    android.app.ActivityThread.handleBindApplication App was starting up, so the ANR was caused by slow app start.

    <ServiceClass>.onCreate()

    [...]

    android.app.ActivityThread.handleCreateService

    Service was being created, so the ANR was likely caused by slow onCreate() code.

    <ServiceClass>.onBind()

    [...]

    android.app.ActivityThread.handleBindService

    Service was being bound, so the ANR was likely caused by slow onBind() code.

    <ServiceClass>.onStartCommand()

    [...]

    android.app.ActivityThread.handleServiceArgs

    Service was being started, so the ANR was likely caused by slow onStartCommand() code.

    For example, if the onStartCommand() method in the MyService class is slow, the main threads will look like this:

    at com.example.app.MyService.onStartCommand(FooService.java:25)
    at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:4820)
    at android.app.ActivityThread.-$$Nest$mhandleServiceArgs(unavailable:0)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2289)
    at android.os.Handler.dispatchMessage(Handler.java:106)
    at android.os.Looper.loopOnce(Looper.java:205)
    at android.os.Looper.loop(Looper.java:294)
    at android.app.ActivityThread.main(ActivityThread.java:8176)
    at java.lang.reflect.Method.invoke(Native method:0)
    

    중요한 함수 호출이 표시되지 않는다면 다음과 같은 몇 가지 다른 가능성이 있습니다.

    • 서비스가 실행 중이거나 종료되고 있습니다. 이는 스택을 너무 늦게 가져왔음을 의미합니다. 이 경우 ANR을 거짓양성으로 무시하면 됩니다.
    • broadcast receiver와 같은 다른 앱 구성요소가 실행 중입니다. 이 경우 기본 스레드가 구성요소에서 차단되어 서비스가 시작되지 않을 수 있습니다.
  3. 주요 함수 호출이 표시되고 ANR이 일반적으로 발생하는 위치를 파악할 수 있으면 기본 스레드 스택의 나머지 부분을 확인하여 느린 작업을 찾아 최적화하거나 중요한 경로에서 삭제하세요.

  4. 서비스에 관한 자세한 내용은 다음 페이지를 참고하세요.

    콘텐츠 제공자가 응답하지 않음

    콘텐츠 제공업체 ANR은 원격 콘텐츠 제공자가 쿼리에 응답하는 데 제한 시간보다 오래 걸려 중단되면 발생합니다.

    기본 제한 시간: ContentProviderClient.setDetectNotResponding를 사용하여 콘텐츠 제공업체에서 지정합니다. ANR 제한 시간에는 원격 콘텐츠 제공업체 쿼리가 실행되는 총 시간이 포함됩니다. 여기에는 원격 앱이 이미 실행되고 있지 않은 경우 원격 앱을 콜드 스타트하는 것이 포함됩니다.

    콘텐츠 제공업체 ANR을 방지하려면 다음 권장사항을 따르세요.

    • 앱이 콘텐츠 제공자를 실행하기 시작하면 ANR 제한 시간에 집계되므로 앱 시작이 빠른지 확인합니다.
    • 콘텐츠 제공자 쿼리가 빠른지 확인합니다.
    • 앱의 모든 바인더 스레드를 차단할 수 있는 동시 차단 바인더 호출을 많이 실행하지 마세요.

    일반적인 원인

    다음 표에는 콘텐츠 제공업체 ANR의 일반적인 원인과 추천 수정사항이 나와 있습니다.

    원인 결과 신호 추천 수정사항
    느린 콘텐츠 제공자 쿼리 콘텐츠 제공자를 실행하는 데 시간이 너무 오래 걸리거나 차단되었습니다. android.content.ContentProvider$Transport.query 프레임은 바인더 스레드에 있습니다. 콘텐츠 제공자 쿼리를 최적화합니다. 바인더 스레드를 차단하는 항목 파악
    느린 앱 시작 콘텐츠 제공자의 앱을 시작하는 데 시간이 너무 오래 걸립니다. ActivityThread.handleBindApplication 프레임이 기본 스레드에 있습니다. 앱 시작을 최적화합니다.
    바인더 스레드 소진 - 모든 바인더 스레드가 사용 중입니다. 모든 바인더 스레드에서 다른 동기 요청을 처리하므로 콘텐츠 제공자 바인더 호출이 실행될 수 없습니다. 앱이 시작되지 않고, 모든 바인더 스레드가 사용 중이며, 콘텐츠 제공자가 실행되고 있지 않습니다. 바인더 스레드의 부하를 줄입니다. 즉, 수신 전화를 처리할 때 동기 발신 바인더 호출을 줄이거나 작업을 줄일 수 있습니다.

    디버깅 방법

    Google Play Console 또는 Firebase Crashlytics의 클러스터 서명 및 ANR 보고서를 사용하여 콘텐츠 제공업체 ANR을 디버그하려면 기본 스레드와 바인더 스레드에서 어떤 작업을 하는지 확인합니다.

    다음 플로우 차트에서는 콘텐츠 제공업체 ANR을 디버그하는 방법을 설명합니다.

    그림 7. 콘텐츠 제공업체 ANR을 디버그하는 방법

    다음 코드 스니펫은 느린 콘텐츠 제공자 쿼리로 인해 차단될 때 바인더 스레드의 모양을 보여줍니다. 이 경우 콘텐츠 제공자 쿼리는 데이터베이스를 열 때 잠금을 대기합니다.

    binder:11300_2 (tid=13) Blocked
    
    Waiting for osm (0x01ab5df9) held by at com.google.common.base.Suppliers$NonSerializableMemoizingSupplier.get(Suppliers:182)
    at com.example.app.MyClass.blockingGetOpenDatabase(FooClass:171)
    [...]
    at com.example.app.MyContentProvider.query(MyContentProvider.java:915)
    at android.content.ContentProvider$Transport.query(ContentProvider.java:292)
    at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:107)
    at android.os.Binder.execTransactInternal(Binder.java:1339)
    at android.os.Binder.execTransact(Binder.java:1275)
    

    다음 코드 스니펫은 느린 앱 시작으로 인해 차단될 때 기본 스레드가 어떻게 표시되는지 보여줍니다. 이 경우 칼리지 초기화 중 잠금 경합으로 인해 앱 시작이 느려집니다.

    main (tid=1) Blocked
    
    [...]
    at dagger.internal.DoubleCheck.get(DoubleCheck:51)
    - locked 0x0e33cd2c (a qsn)at dagger.internal.SetFactory.get(SetFactory:126)
    at com.myapp.Bar_Factory.get(Bar_Factory:38)
    [...]
    at com.example.app.MyApplication.onCreate(DocsApplication:203)
    at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1316)
    at android.app.ActivityThread.handleBindApplication(ActivityThread.java:6991)
    at android.app.ActivityThread.-$$Nest$mhandleBindApplication(unavailable:0)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2235)
    at android.os.Handler.dispatchMessage(Handler.java:106)
    at android.os.Looper.loopOnce(Looper.java:205)
    at android.os.Looper.loop(Looper.java:294)
    at android.app.ActivityThread.main(ActivityThread.java:8170)
    at java.lang.reflect.Method.invoke(Native method:0)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:971)
    

    느린 작업 응답

    앱이 JobService.onStartJob() 또는 JobService.onStopJob()에 응답하는 데 너무 오래 걸리거나 JobService.setNotification()을 사용하여 알림을 제공하는 데 너무 오래 걸리면 작업 응답 ANR이 느려집니다. 이는 앱의 기본 스레드가 다른 작업을 하는 것이 차단되어 있음을 나타냅니다.

    JobService.onStartJob() 또는 JobService.onStopJob() 관련 문제인 경우 기본 스레드에서 어떤 일이 일어나고 있는지 확인하세요. JobService.setNotification() 관련 문제인 경우 최대한 빨리 호출해야 합니다. 알림을 제공하기 전에 많은 작업을 하지 않습니다.

    알 수 없는 ANR

    ANR이 발생하는 이유가 명확하지 않거나 클러스터 서명 및 ANR 보고서에 ANR을 디버그하기 위한 정보가 부족한 경우가 있습니다. 이 경우에도 ANR이 조치 가능한지 확인하기 위해 취할 수 있는 몇 가지 단계가 있습니다.

    Message queue idle 또는 nativePollOnce

    스택에 android.os.MessageQueue.nativePollOnce 프레임이 있다면 응답하지 않는 것으로 의심되는 스레드가 실제로 유휴 상태이고 루퍼 메시지를 기다리고 있음을 나타내는 경우가 많습니다. Google Play Console에서 ANR 세부정보는 다음과 같이 표시됩니다.

    Native method - android.os.MessageQueue.nativePollOnce
    Executing service com.example.app/com.example.app.MyService
    

    예를 들어 기본 스레드가 유휴 상태이면 스택은 다음과 같습니다.

    "main" tid=1 NativeMain threadIdle
    
    #00  pc 0x00000000000d8b38  /apex/com.android.runtime/lib64/bionic/libc.so (__epoll_pwait+8)
    #01  pc 0x0000000000019d88  /system/lib64/libutils.so (android::Looper::pollInner(int)+184)
    #02  pc 0x0000000000019c68  /system/lib64/libutils.so (android::Looper::pollOnce(int, int*, int*, void**)+112)
    #03  pc 0x000000000011409c  /system/lib64/libandroid_runtime.so (android::android_os_MessageQueue_nativePollOnce(_JNIEnv*, _jobject*, long, int)+44)
    at android.os.MessageQueue.nativePollOnce (Native method)
    at android.os.MessageQueue.next (MessageQueue.java:339)  at android.os.Looper.loop (Looper.java:208)
    at android.app.ActivityThread.main (ActivityThread.java:8192)
    at java.lang.reflect.Method.invoke (Native method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:626)
    at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1015)
    

    응답하지 않는 것으로 의심되는 스레드가 유휴 상태일 수 있는 몇 가지 이유는 다음과 같습니다.

    • 지연 스택 덤프. ANR 트리거와 덤프되는 스택 사이의 짧은 기간 동안 복구된 스레드 Android 13의 Pixel에서는 지연 시간이 약 100ms이지만 1초를 초과할 수 있습니다. Android 14의 Pixel 지연 시간은 일반적으로 10ms 미만입니다.
    • 스레드 잘못된 기여 분석. ANR 서명을 빌드하는 데 사용된 스레드가 ANR을 유발한 실제 응답하지 않는 스레드가 아닙니다. 이 경우 ANR이 다음 유형 중 하나인지 확인해 보세요.
    • 시스템 전체 문제. 시스템 부하 과다 또는 시스템 서버 문제로 인해 프로세스가 예약되지 않았습니다.

    스택 프레임 없음

    일부 ANR 보고서에는 ANR 관련 스택이 포함되지 않습니다. 즉, ANR 보고서를 생성할 때 스택 덤프가 실패했다는 의미입니다. 스택 프레임이 누락되는 데는 몇 가지 이유가 있을 수 있습니다.

    • 스택을 가져오는 데 시간이 너무 오래 걸리고 타임아웃됩니다.
    • 스택을 가져오기 전에 프로세스가 종료되었거나 종료되었습니다.
    [...]
    
    --- CriticalEventLog ---
    capacity: 20
    timestamp_ms: 1666030897753
    window_ms: 300000
    
    libdebuggerd_client: failed to read status response from tombstoned: timeout reached?
    
    ----- Waiting Channels: pid 7068 at 2022-10-18 02:21:37.<US_SOCIAL_SECURITY_NUMBER>+0800 -----
    
    [...]
    

    스택 프레임이 없는 ANR은 클러스터 서명 또는 ANR 보고서에서 조치를 취할 수 없습니다. 디버그하려면 앱의 다른 클러스터를 살펴보세요. 문제가 충분히 크면 일반적으로 스택 프레임이 있는 자체 클러스터가 있기 때문입니다. 또 다른 옵션은 Perfetto 트레이스를 살펴보는 것입니다.

    알려진 문제

    ANR이 트리거되기 전에 브로드캐스트 처리를 완료하기 위해 앱의 프로세스에 타이머를 유지하는 것은 시스템에서 ANR을 모니터링하는 비동기 방식으로 인해 제대로 작동하지 않을 수 있습니다.