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의 원인을 확인하는 방법을 보여줍니다.
Play Vitals를 통해 이와 같이 일반적인 ANR 원인 중 일부를 감지하고 디버그할 수 있습니다. 예를 들어 Vitals에서 잠금 경합으로 인해 ANR이 발생했음을 감지하면 ANR 통계 섹션에 문제를 요약하고 권장 해결 방법을 표시할 수 있습니다.
포커스가 설정된 창 없음
터치와 같은 이벤트는 히트 테스트에 기반하여 관련 창으로 직접 전송되지만, 키와 같은 이벤트에는 타겟이 필요합니다. 이 타겟을 포커스가 설정된 창이라고 합니다. 디스플레이마다 포커스가 설정된 창은 하나만 있으며 일반적으로 사용자가 현재 상호작용하고 있는 창입니다. 포커스가 설정된 창을 찾을 수 없는 경우 입력으로 인해 포커스가 설정된 창 없음 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가 브로드캐스트를 제때 처리하지 않을 때 발생합니다. 동기 수신기 또는 goAsync()
를 호출하지 않는 수신기의 경우 시간 초과는 onReceive()
가 제때 완료되지 않았음을 의미합니다. 비동기 수신기 또는 goAsync()
를 호출하는 수신기의 경우 시간 초과는 PendingResult.finish()
가 제때 호출되지 않았음을 의미합니다.
broadcast receiver ANR은 주로 다음 스레드에서 발생합니다.
- 앱 시작이 느린 문제인 경우 기본 스레드
onReceive()
코드가 느린 문제인 경우 broadcast receiver를 실행 중인 스레드goAsync()
브로드캐스트 코드가 느린 문제인 경우 브로드캐스트 작업자 스레드
broadcast receiver ANR을 방지하려면 다음 권장사항을 따르세요.
- 앱이 브로드캐스트를 처리하기 시작하면 ANR 시간 초과에서 집계되므로 앱 시작이 빠른지 확인합니다.
goAsync()
를 사용하는 경우PendingResult.finish()
가 빠르게 호출되는지 확인합니다. 여기에는 동기 broadcast receiver와 동일한 ANR 시간 제한이 적용됩니다.goAsync()
를 사용하는 경우 작업자 스레드를 다른 장기 실행 작업 또는 차단 작업과 공유하지 않아야 합니다.- 기본 스레드에서 실행 중인 UI 코드가 차단되지 않도록
registerReceiver()
을 사용하여 기본이 아닌 스레드에서 broadcast receiver를 실행하는 것이 좋습니다.
제한 시간
브로드캐스트 수신 제한 시간은 포그라운드 인텐트 플래그 설정 여부와 플랫폼 버전에 따라 다릅니다.
인텐트 유형 | Android 13 및 이전 버전 | Android 14 및 이후 버전 |
---|---|---|
포그라운드 우선순위 인텐트 ( |
10초 |
프로세스에 CPU가 부족한지 여부에 따라 10~20초 |
백그라운드 우선순위 인텐트 ( |
60초 |
프로세스에 CPU가 부족한지 여부에 따라 60~120초 |
FLAG_RECEIVER_FOREGROUND
플래그가 설정되어 있는지 확인하려면 ANR 제목에서 'flg='를 찾아 0x10000000
이 있는지 확인합니다. 이 비트가 설정되어 있으면 인텐트에 FLAG_RECEIVER_FOREGROUND
가 설정되어 있는 것이므로 제한 시간이 더 짧습니다.
브로드캐스트 시간 제한이 짧은(10~20초) ANR 제목의 예:
Broadcast of Intent { act=android.inent.action.SCREEN_ON flg=0x50200010 }
브로드캐스트 시간 제한이 긴(60~120초) ANR 제목의 예:
Broadcast of Intent { act=android.intent.action.TIME_SET flg=0x25200010 }
브로드캐스트 시간 측정 방법
브로드캐스트 시간 측정은 브로드캐스트가 system_server
에서 앱으로 전달될 때 시작되고 앱이 브로드캐스트 처리를 마치면 완료됩니다. 앱 프로세스가 아직 실행되고 있지 않다면 ANR 제한 시간 내에 콜드 스타트를 실행해야 합니다. 따라서 앱 시작 속도가 느리면 broadcast receiver ANR이 발생할 수 있습니다.
다음 그림은 broadcast receiver ANR 타임라인이 특정 앱 프로세스와 일치하는지 보여줍니다.
ANR 시간 초과 측정은 broadcast receiver가 브로드캐스트 처리를 완료하면 종료됩니다. 정확한 시점은 동기 수신자인지 비동기 수신자인지에 따라 다릅니다.
- 동기 수신기의 경우
onReceive()
가 반환되면 측정이 중지됩니다. - 비동기 수신기의 경우
PendingResult.finish()
가 호출되면 측정이 중지됩니다.
일반적인 원인
다음은 broadcast receiver ANR의 일반적인 원인과 해결 방법입니다.
원인 | 적용 대상 | 발생한 문제 | 추천 해결 방법 |
---|---|---|---|
느린 앱 시작 | 모든 수신기 | 앱이 콜드 스타트를 수행하는 데 시간이 너무 오래 걸렸습니다. | 느린 앱 시작을 최적화합니다. |
onReceive() 가 예약되지 않음 | 모든 수신기 | broadcast receiver 스레드가 다른 작업을 하느라 onReceive() 메서드를 시작할 수 없습니다. | 수신자 스레드에서 장기 실행 작업을 수행하지 않습니다. 아니면 수신기를 전용 스레드로 옮깁니다. |
느린 onReceive() | 모든 수신기, 주로 동기식 수신기 | onReceive() 메서드가 시작되었지만 차단되었거나 느려서 제때 완료되지 않았습니다. | 느린 수신기 코드를 최적화합니다. |
비동기 수신기 작업이 예약되지 않음 | goAsync() 수신기 | onReceive() 메서드가 차단된 작업자 스레드 풀에서 작업을 실행하려고 하여 작업이 시작되지 않았습니다. |
느린 호출 또는 차단 호출을 최적화하거나 브로드캐스트 작업자와 기타 장기 실행 작업에 다른 스레드를 사용합니다. |
작업이 느리거나 차단됨 | goAsync() 수신기 |
브로드캐스트를 처리하던 중 작업자 스레드 풀 어딘가에 차단되거나 느린 작업이 있었습니다. 따라서 PendingResult.finish 가 제때 호출되지 않았습니다. | 느린 async 수신기 코드를 최적화합니다. |
PendingResult.finish 호출을 잊음 |
goAsync() 수신기 |
코드 경로에서 finish() 호출이 누락되었습니다. |
finish() 가 항상 호출되는지 확인합니다. |
디버깅 방법
클러스터 서명 및 ANR 보고서에 따라 수신기가 실행되는 스레드를 찾은 다음 누락되거나 느리게 실행되는 특정 코드를 찾을 수 있습니다.
다음 플로우 차트는 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]' 스레드 풀이 이 브로드캐스트를 처리하기 위한 기본 작업을 실행하는지 확인할 수 있습니다. 스택 덤프를 살펴보면 네 개의 백그라운드(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()
, andonBind()
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.
If you've determined that the execute service ANR is actionable, follow these steps to help resolve the issue:
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
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 theMyService
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와 같은 다른 앱 구성요소가 실행 중입니다. 이 경우 이 구성요소에서 기본 스레드가 차단되어 서비스가 시작되지 않을 수 있습니다.
키 함수 호출이 표시되고 ANR이 일반적으로 발생하는 위치를 파악할 수 있으면 기본 스레드 스택의 나머지 부분을 확인하여 느린 작업을 찾아 최적화하거나 중요 경로 외부로 옮깁니다.
- 앱이 콘텐츠 제공업체를 실행하기 시작하면 ANR 시간 초과에서 집계되므로 앱 시작이 빠른지 확인합니다.
- 콘텐츠 제공업체 쿼리가 빠른지 확인합니다.
- 앱의 모든 바인더 스레드를 차단할 수 있으므로 차단 바인더 호출을 동시에 많이 실행하지 않습니다.
- 지연 스택 덤프. ANR 트리거와 덤프되는 스택 사이의 짧은 기간 동안 스레드가 복구되었습니다. Android 13의 Pixel 지연 시간은 약 100ms이지만 1초를 초과할 수도 있습니다. Android 14의 Pixel 지연 시간은 일반적으로 10ms 미만입니다.
- 스레드의 잘못된 기여 분석. ANR 서명을 빌드하는 데 사용된 스레드가 실제로 응답하지 않아 ANR을 일으킨 스레드가 아니었습니다. 이 경우 ANR이 다음 유형 중 하나인지 확인해 보세요.
- 시스템 전체 문제. 과도한 시스템 부하 또는 시스템 서버의 문제로 인해 프로세스가 예약되지 않은 경우
- 스택을 가져오는 데 시간이 너무 오래 걸려 제한 시간이 초과되었습니다.
- 스택을 가져오기 전에 프로세스가 종료되었습니다.
서비스에 관한 자세한 내용은 다음 페이지를 참고하세요.
콘텐츠 제공자가 응답하지 않음
콘텐츠 제공업체 ANR은 원격 콘텐츠 제공업체가 쿼리에 응답하는 데 제한 시간보다 오래 걸려서 중단된 경우 발생합니다.
기본 제한 시간은 ContentProviderClient.setDetectNotResponding
을 사용하여 콘텐츠 제공업체에서 지정합니다. ANR 제한 시간에는 원격 콘텐츠 제공업체 쿼리가 실행되는 데 걸린 총 시간이 포함되며, 여기에는 원격 앱이 아직 실행되고 있지 않은 경우 원격 앱을 콜드 스타트하는 시간도 포함됩니다.
콘텐츠 제공업체 ANR을 방지하려면 다음 권장사항을 따르세요.
일반적인 원인
다음 표에는 콘텐츠 제공업체 ANR의 일반적인 원인과 추천 해결 방법이 나와 있습니다.
원인 | 결과 | 신호 | 추천 해결 방법 |
---|---|---|---|
느린 콘텐츠 제공업체 쿼리 | 콘텐츠 제공업체를 실행하는 데 시간이 너무 오래 걸리거나 콘텐츠 제공업체가 차단되었습니다. | android.content.ContentProvider$Transport.query 프레임이 바인더 스레드에 있습니다. |
콘텐츠 제공업체 쿼리를 최적화합니다. 바인더 스레드를 차단하는 요소를 찾습니다. |
느린 앱 시작 | 콘텐츠 제공업체 앱을 시작하는 데 시간이 너무 오래 걸립니다. | ActivityThread.handleBindApplication 프레임이 기본 스레드에 있습니다. |
앱 시작을 최적화합니다. |
바인더 스레드 소진 - 모든 바인더 스레드가 사용 중임 | 모든 바인더 스레드가 다른 동기식 요청을 처리하느라 콘텐츠 제공업체 바인더 호출을 실행할 수 없습니다. | 앱이 시작되지 않고, 모든 바인더 스레드가 사용 중이며, 콘텐츠 제공업체가 실행되지 않습니다. | 바인더 스레드의 부하를 줄입니다. 즉, 동기식 발신 바인더 호출을 줄이거나 수신 호출을 처리할 때 실행하는 작업을 줄입니다. |
디버깅 방법
Google Play Console 또는 Firebase Crashlytics의 클러스터 서명 및 ANR 보고서를 사용하여 콘텐츠 제공업체 ANR을 디버그하려면 기본 스레드와 바인더 스레드에서 어떤 작업을 수행하고 있는지 살펴봅니다.
다음 플로우 차트에서는 콘텐츠 제공업체 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)
다음 코드 스니펫은 느린 앱 시작으로 인해 차단될 경우 기본 스레드가 어떤 모습인지 보여줍니다. 이 경우 dagger 초기화 중 잠금 경합으로 인해 앱 시작이 느려집니다.
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 보고서에는 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을 모니터링하는 비동기 방식으로 인해 제대로 작동하지 않을 수 있습니다.