Android-ANR은 어떻게 조사합니까?


153

내 앱에서 ANR (응용 프로그램이 응답하지 않음)을 던진 위치를 찾는 방법이 있습니까? / data의 traces.txt 파일을 살펴본 결과 내 응용 프로그램에 대한 추적이 표시됩니다. 이것이 내가 추적에서 보는 것입니다.

DALVIK THREADS:
"main" prio=5 tid=3 TIMED_WAIT
  | group="main" sCount=1 dsCount=0 s=0 obj=0x400143a8
  | sysTid=691 nice=0 sched=0/0 handle=-1091117924
  at java.lang.Object.wait(Native Method)
  - waiting on <0x1cd570> (a android.os.MessageQueue)
  at java.lang.Object.wait(Object.java:195)
  at android.os.MessageQueue.next(MessageQueue.java:144)
  at android.os.Looper.loop(Looper.java:110)
  at android.app.ActivityThread.main(ActivityThread.java:3742)
  at java.lang.reflect.Method.invokeNative(Native Method)
  at java.lang.reflect.Method.invoke(Method.java:515)
  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:739)
  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:497)
  at dalvik.system.NativeStart.main(Native Method)

"Binder Thread #3" prio=5 tid=15 NATIVE
  | group="main" sCount=1 dsCount=0 s=0 obj=0x434e7758
  | sysTid=734 nice=0 sched=0/0 handle=1733632
  at dalvik.system.NativeStart.run(Native Method)

"Binder Thread #2" prio=5 tid=13 NATIVE
  | group="main" sCount=1 dsCount=0 s=0 obj=0x433af808
  | sysTid=696 nice=0 sched=0/0 handle=1369840
  at dalvik.system.NativeStart.run(Native Method)

"Binder Thread #1" prio=5 tid=11 NATIVE
  | group="main" sCount=1 dsCount=0 s=0 obj=0x433aca10
  | sysTid=695 nice=0 sched=0/0 handle=1367448
  at dalvik.system.NativeStart.run(Native Method)

"JDWP" daemon prio=5 tid=9 VMWAIT
  | group="system" sCount=1 dsCount=0 s=0 obj=0x433ac2a0
  | sysTid=694 nice=0 sched=0/0 handle=1367136
  at dalvik.system.NativeStart.run(Native Method)

"Signal Catcher" daemon prio=5 tid=7 RUNNABLE
  | group="system" sCount=0 dsCount=0 s=0 obj=0x433ac1e8
  | sysTid=693 nice=0 sched=0/0 handle=1366712
  at dalvik.system.NativeStart.run(Native Method)

"HeapWorker" daemon prio=5 tid=5 VMWAIT
  | group="system" sCount=1 dsCount=0 s=0 obj=0x4253ef88
  | sysTid=692 nice=0 sched=0/0 handle=1366472
  at dalvik.system.NativeStart.run(Native Method)

----- end 691 -----

문제가 어디에 있는지 어떻게 알 수 있습니까? 추적의 메소드는 모두 SDK 메소드입니다.

감사.


2
에 이런 종류의 보고서가 하나 android.os.MessageQueue.nativePollOnce(Native Method)있습니다. 안전하게 무시해도 되나요?
rds

답변:


124

"메인"스레드에서 약간의 긴 작업이 수행 될 때 ANR이 발생합니다. 이것은 이벤트 루프 스레드이며, 사용중인 경우 Android는 애플리케이션에서 더 이상 GUI 이벤트를 처리 할 수 ​​없으므로 ANR 대화 상자를 발생시킵니다.

자, 당신이 게시 한 추적에서 주 스레드가 정상적으로 작동하는 것 같습니다. 문제가 없습니다. MessageQueue에서 유휴 상태가되어 다른 메시지가 들어 오기를 기다리고 있습니다. ANR이 스레드를 영구적으로 차단 한 것보다 더 긴 작업 일 가능성이 있으므로 작업이 완료된 후 이벤트 스레드가 복구되고 추적이 진행되었습니다. ANR 후.

영구 블록 (예를 들어 교착 상태를 획득하는 교착 상태) 인 경우 ANR이 발생하는 위치를 감지하는 것이 쉽지만 일시적인 지연 일 경우에는 더 어렵습니다. 먼저 코드를 살펴보고 취약한 지점과 장기 실행 작업을 찾으십시오. 예를 들어 이벤트 스레드 내에서 소켓, 잠금, 스레드 휴면 및 기타 차단 작업을 사용할 수 있습니다. 이 모든 것이 별도의 스레드에서 발생하는지 확인해야합니다. 문제가없는 것 같으면 DDMS를 사용하고 스레드보기를 활성화하십시오. 추적 결과와 유사한 애플리케이션의 모든 스레드가 표시됩니다. ANR을 재현하고 동시에 메인 스레드를 새로 고칩니다. ANR 시점에 무슨 일이 일어나고 있는지 정확하게 보여 주어야합니다.


6
유일한 문제는 "ARR 재생산"입니다 :-). 스택 트레이스 쇼의 주요 스레드가 어떻게 유휴 상태인지 설명해 주시겠습니까?
Blundell

20
스택 추적은 기본 스레드가 Looper (메시지 루프 구현)에 있고 Object.wait를 통해 시간이 초과 된 대기를 수행함을 보여줍니다. 이는 메시지 루프에 현재 발송할 메시지가없고 새 메시지가 들어 오기를 기다리는 중임을 의미합니다. ANR은 시스템에서 메시지 루프가 메시지를 처리하는 데 많은 시간을 소비하고 다른 메시지를 처리하지 않는 경우 열. 루프가 메시지를 기다리는 중이라면 분명히 이런 일이 일어나지 않습니다.
sooniln

3
@Soonil 안녕하세요 섹션의 나머지 부분이 바인더 스레드 3, 바인더 스레드 2 JDWP 악마 프리 오 5와 같은 의미를 알고 있습니까? sCount, dsCount, obj, sysTid, 멋진 sched 수단은 무엇입니까? 또한 VMWAIT, RUNNABLE, NATIVE와 같은 정보가 있습니다
minhaz

1
내 앱은 NDK 기반이며 동일한 ANR이 표시됩니다. 또한 주 실은 괜찮습니다. DDMS를 시도하고 작업자 스레드가 중지되면 새로 고칩니다. 불행히도 내가 얻는 한 줄의 NativeStart :: run입니다. DDMS 스레드보기는 기본 NDK 스레드를 검사 할 수 있습니까? 또한 : StrictMode가 아무것도 찾지 못했습니다.
Bram

6
출력에 대한 자세한 설명은 elliotth.blogspot.com/2012/08/… 를 참조하십시오 .
sooniln

96

API 레벨 9 이상에서 StrictMode 를 사용할 수 있습니다 .

엄격 모드는 UI 작업이 수신되고 애니메이션이 발생하는 응용 프로그램의 메인 스레드에서 우발적 인 디스크 또는 네트워크 액세스를 포착하는 데 가장 일반적으로 사용됩니다. 응용 프로그램의 기본 스레드를 반응 적으로 유지 하면 ANR 대화 상자 가 사용자에게 표시되지 않습니다.

public void onCreate() {
    StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
                           .detectAll()
                           .penaltyLog()
                           .penaltyDeath()
                           .build());
    super.onCreate();
}

를 사용 penaltyLog()하면 응용 프로그램을 사용하여 위반이 발생할 때 adb logcat의 출력을 볼 수 있습니다.


StrictMode는 유형으로 해석 될 수 없습니다. 먼저 가져와야 할 것이 있습니까? Ctrl + Shift + O를 눌러도 도움이되지 않습니다.
kuchi

23
작은 팁
-if

@uval "제작에 포함되는 것을 막으려면"은 무슨 뜻입니까? !!
Muhammed Refaat

2
@MuhammedRefaat ANR을 막지 않습니다. 5 초 후에 앱이 즉시 중단됩니다. 예를 들어 메인 스레드에서 데이터베이스에 액세스하고 2 초가 걸리면 ANR은받지 않지만 StrictMode는 앱을 중단시킵니다. StrictMode는 프로덕션이 아니라 디버깅 단계를위한 것입니다.
Amir Uval

1
@MuhammedRefaat가 귀하의 질문에 대한 답변을 추가했습니다.
Amir Uval

80

어떤 작업이 UI 스레드를 보유하는지 궁금합니다. 추적 파일은 작업을 찾는 힌트를 제공합니다. 각 스레드의 상태를 조사해야합니다.

실 상태

  • 실행 중-애플리케이션 코드 실행
  • 수면-Thread.sleep ()
  • 모니터-모니터 잠금 획득 대기
  • wait-Object.wait ()에서
  • native-네이티브 코드 실행
  • vmwait-VM 자원 대기
  • 좀비-실이 죽어 가고있다
  • init-스레드가 초기화 중입니다 (이것을 보지 않아야합니다)
  • 시작-스레드가 시작됩니다 (이것도 보지 않아야합니다)

SUSPENDED, MONITOR 상태에 중점을 둡니다. 모니터 상태는 어떤 스레드를 조사했는지를 나타내며 스레드의 SUSPENDED 상태는 교착 상태의 주요 원인 일 수 있습니다.

기본 조사 단계

  1. "잠금 대기 중"찾기
    • 모니터 상태 "바인더 스레드 # 15"를 찾을 수 있습니다. prio = 5 tid = 75 MONITOR
    • "잠금 대기 중"을 찾으면 운이 좋다
    • 예 : threadid = 74가 보유한 <0xblahblah> (com.foo.A) 잠금 대기 중
  2. "tid = 74"는 이제 작업을 보유하고 있음을 알 수 있습니다. tid = 74로 가십시오
  3. tid = 74 아마도 일시 중단 된 상태입니다! 주된 이유를 찾으십시오!

추적에 항상 "잠금 대기 중"이 포함되는 것은 아닙니다. 이 경우 주된 이유를 찾기가 어렵습니다.


1
좋은 설명입니다. 이제 ANR 로그를 이해하기가 더 쉬워졌습니다. 그러나 여전히 1 단계에서 스레드 ID를 쉽게 찾을 수 있기 때문에 원인을 이해하는 데 문제가 있습니다 .2 단계에서 2 단계에서 상태를 확인하려고 할 때 찾을 수 없습니다. . 그것을 진행하는 방법에 대한 아이디어가 있습니까?
THZ

1
나는 - waiting to lock an unknown object안에있다 "HeapTaskDaemon" daemon prio=5 tid=8 Blocked . 누군가 도울 수있는 것은 무엇입니까?
Hilal

13

나는 지난 몇 달 동안 안드로이드를 배웠으므로 전문가와는 거리가 멀지 만 ANR에 대한 문서에 정말 실망했습니다.

대부분의 조언은 코드를 맹목적으로 살펴봄으로써 코드를 피하거나 수정하는 데 도움이되는 것 같습니다.하지만 추적을 분석 할 때 아무것도 찾을 수 없습니다.

ANR 로그에서 실제로 찾아야 할 세 가지가 있습니다.

1) 교착 상태 : 스레드가 WAIT 상태 인 경우 세부 정보를 통해 누가 "heldby ="인지 확인할 수 있습니다. 대부분 자체적으로 유지되지만 다른 스레드에 의해 유지되는 경우 위험 신호일 수 있습니다. 그 실을보고 그것이 무엇을 가지고 있는지보십시오. 루프가 발견 될 수 있는데, 이는 무언가 잘못되었다는 명백한 신호입니다. 이것은 매우 드물지만 발생하면 악몽이기 때문에 첫 번째 포인트입니다.

2) 메인 스레드 대기 : 메인 스레드가 WAIT 상태 인 경우 다른 스레드에 의해 고정되어 있는지 확인하십시오. UI 스레드가 백그라운드 스레드로 유지되어서는 안되기 때문에 이런 일이 발생해서는 안됩니다.

이 두 시나리오 모두 코드를 크게 재 작업해야한다는 것을 의미합니다.

3) 메인 스레드에서의 과도한 작업 : 이것이 ANR의 가장 일반적인 원인이지만 때로는 찾아서 수정하기 어려운 것 중 하나입니다. 주요 스레드 세부 사항을보십시오. 스택 추적을 아래로 스크롤하여 앱에서 인식하는 클래스가 표시 될 때까지 스크롤합니다. 추적에서 메소드를보고이 위치에서 네트워크 호출, db 호출 등을 작성하는지 확인하십시오.

마지막으로, 나는 내 자신의 코드를 뻔뻔스럽게 꽂아서 죄송합니다. https://github.com/HarshEvilGeek/Android-Log-Analyzer 에서 작성한 파이썬 로그 분석기를 사용할 수 있습니다. 로그 파일을 통해 ANR 파일을 열고, 찾을 수 있습니다. 교착 상태, 대기중인 기본 스레드 찾기, 에이전트 로그에서 포착되지 않은 예외 찾기 및 비교적 읽기 쉬운 방식으로 화면에 모두 인쇄하십시오. ReadMe 파일 (추가하려고하는 파일)을 읽고 사용법을 배우십시오. 지난주에 많은 도움이되었습니다!


4

타이밍 문제를 분석 할 때마다 중단 점에서 앱을 정지하면 문제가 해결되므로 디버깅이 도움이되지 않습니다.

가장 좋은 방법은 많은 로깅 호출 (Log.XXX ())을 앱의 다른 스레드 및 콜백에 삽입하고 지연이 어디 있는지 확인하는 것입니다. 스택 추적이 필요한 경우 새 예외를 만들고 (인스턴스화) 예외를 기록하십시오.


2
스택 추적이 필요한 경우 새 예외를 만드는 데 대한 조언을 주셔서 감사합니다. 그것은 디버깅 할 때 매우 유용합니다 :)
kuchi

3

무엇이 ANR을 유발합니까?

일반적으로 시스템은 응용 프로그램이 사용자 입력에 응답 할 수없는 경우 ANR을 표시합니다.

앱이 잠재적으로 긴 작업을 수행하는 모든 상황에서 UI 스레드에 대한 작업을 수행하지 말고 대신 작업자 스레드를 생성하고 대부분의 작업을 수행해야합니다. 이렇게하면 UI 스레드 (사용자 인터페이스 이벤트 루프를 구동)가 계속 실행되고 시스템이 코드가 정지 된 것으로 결론을 내릴 수 없습니다.

ANR을 피하는 방법

Android 애플리케이션은 일반적으로 기본적으로 "UI 스레드"또는 "주 스레드"로 단일 스레드에서 완전히 실행됩니다. 이는 애플리케이션이 입력 이벤트 또는 인 텐트 브로드 캐스트를 처리 할 기회를 제공하지 않기 때문에 완료하는 데 오랜 시간이 걸리는 애플리케이션이 UI 스레드에서 수행하는 모든 작업이 ANR 대화 상자를 트리거 할 수 있음을 의미합니다.

따라서 UI 스레드에서 실행되는 모든 메소드는 해당 스레드에서 가능한 한 적은 작업을 수행해야합니다. 특히, 활동은 onCreate () 및 onResume ()과 같은 주요 수명주기 메소드에서 설정하기 위해 가능한 한 적게해야합니다. 네트워크 또는 데이터베이스 작업과 같은 잠재적으로 오래 실행되는 작업 또는 비트 맵 크기 조정과 같은 계산 비용이 많이 드는 계산은 작업자 스레드 (또는 데이터베이스 작업의 경우 비동기 요청을 통해)에서 수행해야합니다.

코드 : AsyncTask 클래스가있는 작업자 스레드

private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
    // Do the long-running work in here
    protected Long doInBackground(URL... urls) {
        int count = urls.length;
        long totalSize = 0;
        for (int i = 0; i < count; i++) {
            totalSize += Downloader.downloadFile(urls[i]);
            publishProgress((int) ((i / (float) count) * 100));
            // Escape early if cancel() is called
            if (isCancelled()) break;
        }
        return totalSize;
    }

    // This is called each time you call publishProgress()
    protected void onProgressUpdate(Integer... progress) {
        setProgressPercent(progress[0]);
    }

    // This is called when doInBackground() is finished
    protected void onPostExecute(Long result) {
        showNotification("Downloaded " + result + " bytes");
    }
}

코드 : 작업자 스레드 실행

이 작업자 스레드를 실행하려면 단순히 인스턴스를 만들고 execute ()를 호출하십시오.

new DownloadFilesTask().execute(url1, url2, url3);

출처

http://developer.android.com/training/articles/perf-anr.html


1

ANR에 대한 내 문제는 많은 작업 후 스레드가 예외를 반환하는 대신 레이아웃에 존재하지 않는 리소스를 호출하고 있음을 발견했으며 ANR을 얻었습니다 ...


그것은 매우 이상합니다
Nilabja


0

@Horyun Lee 답변의 기본 사항에서 ANR을 조사하는 데 도움이 되는 작은 파이썬 스크립트 를 작성했습니다.traces.txt .

시스템에 graphviz설치 한 경우 ANR이 그래픽으로 출력됩니다 grapvhviz.

$ ./anr.py --format png ./traces.txt

file에서 ANR이 감지되면 png는 아래와 같이 출력됩니다 traces.txt. 더 직관적입니다.

여기에 이미지 설명을 입력하십시오

traces.txt위에서 사용한 샘플 파일은 여기 에서 가져 왔습니다 .


0

ANR- 워치 독 라이브러리를 사용하여 ANR 스택 추적을 정확하게 세부적으로 추적하고 캡처하십시오. 그런 다음 충돌보고 라이브러리로 보낼 수 있습니다. setReportMainThreadOnly()이 시나리오에서 사용 하는 것이 좋습니다 . 앱이 정지 점을 치명적이지 않은 예외로 처리하도록하거나 ANR이 발생하면 앱이 강제 종료 될 수 있습니다.

Google Play 개발자 콘솔로 전송 된 표준 ANR 보고서는 정확한 문제를 정확히 지적하기에 충분하지 않은 경우가 많습니다. 이것이 타사 라이브러리가 필요한 이유입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.