응답하지 않는 대화목록 찾기

이 문서에서는 ANR 스택 덤프에서 응답하지 않는 스레드를 식별하는 방법을 보여줍니다. 응답하지 않는 스레드는 다음 표와 같이 ANR 유형에 따라 다릅니다.

ANR 유형 응답하지 않는 스레드
입력 전달 기본 스레드
입력 전달에 포커스가 맞춰진 창 없음 기본 스레드. 이러한 유형의 ANR은 일반적으로 차단된 스레드로 인해 발생하지 않습니다.
broadcast receiver(동기) onReceive()를 실행 중인 스레드. 기본 스레드가 아닌 스레드의 맞춤 핸들러가 Context.registerReceiver를 사용하여 지정되지 않는 한 기본 스레드입니다.
broadcast receiver(비동기) 코드를 살펴보고 goAsync가 호출된 후 브로드캐스트를 처리하는 작업을 담당하는 스레드나 스레드 풀을 확인합니다.
서비스 제한 시간 실행 중 기본 스레드
포그라운드 서비스 시작 기본 스레드
콘텐츠 제공자가 응답하지 않음 둘 중 하나의 경우입니다.
  • 느린 콘텐츠 제공자 쿼리로 인해 ANR이 발생한 경우 바인더 스레드
  • 긴 앱 시작으로 인해 ANR이 발생한 경우 기본 스레드
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를 실행합니다.