이 문서에서는 ANR 스택 덤프에서 응답하지 않는 스레드를 식별하는 방법을 보여줍니다. 응답하지 않는 스레드는 다음 표와 같이 ANR 유형에 따라 다릅니다.
ANR 유형 | 응답하지 않는 스레드 |
---|---|
입력 전달 | 기본 스레드 |
입력 전달에 포커스가 맞춰진 창 없음 | 기본 스레드. 이러한 유형의 ANR은 일반적으로 차단된 스레드로 인해 발생하지 않습니다. |
broadcast receiver(동기) | onReceive() 를 실행 중인 스레드.
기본 스레드가 아닌 스레드의 맞춤 핸들러가 Context.registerReceiver 를 사용하여 지정되지 않는 한 기본 스레드입니다. |
broadcast receiver(비동기) | 코드를 살펴보고 goAsync 가 호출된 후 브로드캐스트를 처리하는 작업을 담당하는 스레드나 스레드 풀을 확인합니다. |
서비스 제한 시간 실행 중 | 기본 스레드 |
포그라운드 서비스 시작 | 기본 스레드 |
콘텐츠 제공자가 응답하지 않음 | 둘 중 하나의 경우입니다.
|
onStartJob 또는 onStopJob 에 대한 응답 없음 |
기본 스레드 |
다른 스레드나 프로세스의 근본 원인으로 인해 스레드가 응답하지 않는 경우도 있습니다. 다음 사항을 기다리고 있기 때문에 스레드가 응답하지 않을 수 있습니다.
- 다른 스레드가 보유한 잠금
- 다른 프로세스에 대한 느린 바인더 호출
응답하지 않는 스레드의 일반적인 원인
다음은 스레드가 응답하지 않는 일반적인 원인입니다.
느린 바인더 호출
대부분의 바인더 호출은 빠르지만 롱테일은 매우 느릴 수 있습니다. 이러한 상황은 기기가 로드되거나 바인더 응답 스레드가 느린 경우(예: 잠금 경합, 여러 수신 바인더 호출 또는 하드웨어 추상화 계층(HAL) 제한 시간) 발생할 가능성이 높습니다.
이 문제는 가능한 경우 동기 바인더 호출을 백그라운드 스레드로 이동하여 해결할 수 있습니다. 호출이 기본 스레드에서 발생해야 하면 호출이 느린 이유를 찾아보세요. 이를 위한 가장 좋은 방법은 Perfetto 트레이스를 사용하는 것입니다.
스택에서 BinderProxy.transactNative
또는 Binderproxy.transact
를 찾습니다.
이는 바인더 호출이 진행 중임을 의미합니다. 이 두 줄을 따르면 호출되는 바인더 API를 확인할 수 있습니다. 다음 예에서는 IAccessibilityManager.addClient
를 호출합니다.
main tid=123
...
android.os.BinderProxy.transactNative (Native method)
android.os.BinderProxy.transact (BinderProxy.java:568)
android.view.accessibility.IAccessibilityManager$Stub$Proxy.addClient (IAccessibilityManager.java:599)
...
바인더를 연속으로 여러 번 호출
짧은 시간 동안 연속해서 바인더 호출을 여러 번 실행하면 스레드가 오랫동안 차단될 수 있습니다.
차단 I/O
기본 스레드에서는 절대 차단 I/O를 실행해서는 안 됩니다. 이는 안티패턴입니다.
잠금 경합
잠금을 획득할 때 스레드가 차단되면 ANR이 발생할 수 있습니다.
다음 예는 잠금을 획득하려고 할 때 기본 스레드가 차단되는 것을 보여줍니다.
main (tid=1) Blocked
Waiting for com.example.android.apps.foo.BarCache (0x07d657b7) held by
ptz-rcs-28-EDITOR_REMOTE_VIDEO_DOWNLOAD
[...]
at android.app.ActivityThread.handleStopActivity(ActivityThread.java:5412)
[...]
차단 스레드가 동영상을 다운로드하기 위해 HTTP 요청을 하고 있습니다.
ptz-rcs-28-EDITOR_REMOTE_VIDEO_DOWNLOAD (tid=110) Waiting
at jdk.internal.misc.Unsafe.park(Native method:0)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:211)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:715)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireSharedInterruptibly(AbstractQueuedSynchronizer.java:1047)
at java.util.concurrent.CountDownLatch.await(CountDownLatch.java:230)
at com.example.android.apps.foo.HttpRequest.execute(HttpRequest:136)
at com.example.android.apps.foo$Task$VideoLoadTask.downloadVideoToFile(RequestExecutor:711)
[...]
비용이 많이 드는 프레임
다음과 같이 단일 프레임에서 너무 많은 항목을 렌더링하면 프레임 지속 시간 동안 기본 스레드가 응답하지 않을 수 있습니다.
- 불필요하게 화면 밖의 많은 항목을 렌더링합니다.
- 다수의 UI 요소를 렌더링할 때
O(n^2)
와 같은 비효율적인 알고리즘을 사용합니다.
다른 구성요소에 의해 차단됨
broadcast receiver와 같은 다른 구성요소가 5초 넘게 기본 스레드를 차단하면 입력 전달 ANR 및 심각한 버벅거림이 발생할 수 있습니다.
앱 구성요소의 기본 스레드에서 과도한 작업을 실행하지 마세요. 가능하면 다른 스레드에서 broadcast receiver를 실행합니다.