Android - как мне исследовать ANR?

153

Есть ли способ узнать, где мое приложение выбросило ANR (приложение не отвечает). Я взглянул на файл traces.txt в / data и увидел след для своего приложения. Это то, что я вижу в след.

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.

Спасибо.

lostInTransit
источник
2
У меня есть одно сообщение такого рода, также происходит в android.os.MessageQueue.nativePollOnce(Native Method). Могу ли я безопасно проигнорировать это?
Rds

Ответы:

124

ANR происходит, когда в «основном» потоке выполняется какая-то длительная операция. Это поток цикла событий, и если он занят, Android не может обрабатывать какие-либо дальнейшие события GUI в приложении и, таким образом, выдает диалог ANR.

Теперь, в трассе, которую вы разместили, основной поток, кажется, работает нормально, проблем нет. Он находится в бездействии в MessageQueue, ожидая поступления другого сообщения. В вашем случае ANR, скорее всего, была более длительной операцией, а не чем-то, что постоянно блокировало поток, поэтому поток событий восстанавливался после завершения операции, и ваша трассировка прошла после ANR.

Определить, где происходят ANR, легко, если это постоянный блок (например, тупик, получающий некоторые блокировки), но сложнее, если это просто временная задержка. Сначала просмотрите код и найдите уязвимые места и длительные операции. Примеры могут включать в себя использование сокетов, блокировок, спящих потоков и других операций блокировки из потока событий. Вы должны убедиться, что все это происходит в отдельных потоках. Если ничто не кажется проблемой, используйте DDMS и включите представление потока. Это показывает все потоки в вашем приложении, похожие на ваш след. Воспроизведите ANR и обновите основной поток одновременно. Это должно показать вам точно, что происходит во время ANR

sooniln
источник
6
Единственная проблема - «воспроизвести ANR» :-). Не могли бы вы объяснить, как основной поток этой трассировки в стеке - это «холостой ход», это было бы здорово.
Бланделл
20
Трассировка стека показывает, что основной поток находится в Looper (реализация цикла сообщений) и выполняет временное ожидание через Object.wait. Это означает, что в цикле сообщений в настоящее время нет сообщений для отправки, и он ожидает поступления новых сообщений. ANR возникает, когда система понимает, что цикл обработки сообщений тратит много времени на обработку сообщения, а не обрабатывает другие сообщения в очередь. Если циклы ждут сообщений, очевидно, этого не происходит.
скоро
3
@Soonil Привет, вы знаете, что означают остальные разделы, такие как Binder-поток 3, Binder-поток 2 JDWP demon prio 5. Что такое sCount, dsCount, obj, sysTid, хороший sched означает. также у него есть такая информация, как VMWAIT, RUNNABLE,
NATIVE
1
Мое приложение на основе NDK, я вижу тот же самый ANR. Кроме того, основной поток в порядке. Я попробовал DDMS и обновил мой рабочий поток, когда он зависает. К сожалению, все, что я получаю, это одна строка NativeStart :: run. Может ли просмотр потоков DDMS даже проверять нативные потоки NDK? Также: StrictMode ничего не нашел.
Брэм
6
См. Elliotth.blogspot.com/2012/08/… для хорошего объяснения результатов.
скором времени
96

Вы можете включить StrictMode на уровне API 9 и выше.

StrictMode чаще всего используется для обнаружения случайного доступа к диску или сети в главном потоке приложения, где принимаются операции пользовательского интерфейса и происходят анимации. Поддерживая адаптацию основного потока вашего приложения, вы также предотвращаете показ пользователям диалогов ANR .

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

используя его, penaltyLog()вы можете наблюдать за выводом adb logcat, пока вы используете свое приложение, чтобы видеть нарушения по мере их возникновения.

Дхирадж Вепакомма
источник
StrictMode не может быть преобразован в тип. Что мне нужно импортировать в первую очередь? Нажатие CTRL + SHIFT + O не помогает.
Кучи
23
Небольшой совет - используйте if (BuildConfig.DEBUG) ... для предотвращения включения в производство
Амир Увал
@uval что вы подразумеваете под "предотвратить включение в производство"? !!
Мухаммед Рефаат
2
@MuhammedRefaat это не предотвращает ANR. Это приведет к сбою приложения сразу после 5 секунд. Например, если вы обращаетесь к базе данных в главном потоке, и это занимает 2 секунды, вы не получите ANR, но StrictMode приведет к сбою приложения. StrictMode строго для вашей фазы отладки, а не производства.
Амир Увал
1
@MuhammedRefaat добавил мой ответ на ваш вопрос.
Амир Увал
80

Вы задаетесь вопросом, какая задача содержит поток пользовательского интерфейса. Файл трассировки дает вам подсказку, чтобы найти задачу. вам нужно исследовать состояние каждого потока

Состояние потока

  • выполняется - выполняется код приложения
  • спать - называется Thread.sleep ()
  • монитор - ожидание получения блокировки монитора
  • подождите - в Object.wait ()
  • native - выполнение нативного кода
  • vmwait - ожидание на ресурсе виртуальной машины
  • зомби - нить умирает
  • init - поток инициализируется (вы не должны видеть это)
  • начальный - поток собирается начать (вы не должны видеть это тоже)

Сосредоточьтесь на состоянии SUSPENDED, MONITOR. Состояние монитора указывает, какой поток исследуется, и состояние потока SUSPENDED, вероятно, является основной причиной тупика.

Основные этапы расследования

  1. Найти "ожидание блокировки"
    • Вы можете найти состояние монитора "Binder Thread # 15" prio = 5 tid = 75 MONITOR
    • вам повезло, если вы нашли "ждущую блокировки"
    • пример: ожидание блокировки <0xblahblah> (com.foo.A), удерживаемой threadid = 74
  2. Вы можете заметить, что «tid = 74» теперь держит задачу. Так что идите к tid = 74
  3. tid = 74 возможно состояние SUSPENDED! найти основную причину!

Трассировка не всегда содержит «ожидание блокировки». в этом случае трудно найти основную причину.

Хорюн Ли
источник
1
Хорошее объяснение. Теперь мне легче понимать журналы ANR. Но у меня все еще есть проблема, чтобы понять причину, потому что на шаге 1 я могу легко найти идентификатор потока, но когда, на шаге 2, я пытаюсь перейти туда, где он есть, чтобы проверить состояние, я не могу его найти , Есть идеи, как это сделать?
THZ
1
У меня - waiting to lock an unknown objectвнутри "HeapTaskDaemon" daemon prio=5 tid=8 Blocked . Что это может помочь?
Хилал
13

Я изучал Android в течение последних нескольких месяцев, так что я далеко не эксперт, но я был очень разочарован документацией по ANR.

Большая часть рекомендаций, похоже, направлена ​​на то, чтобы их избегать или исправлять, слепо просматривая ваш код, и это здорово, но я ничего не смог найти по анализу трассировки.

Есть три вещи, которые вы действительно должны искать с журналами ANR.

1) Взаимные блокировки: когда поток находится в состоянии WAIT, вы можете просмотреть подробную информацию, чтобы определить, кто из них «holdby =». Большую часть времени он будет удерживаться сам по себе, но если он удерживается другим потоком, это может быть признаком опасности. Иди посмотри на эту ветку и посмотри, чем она держится. Вы можете найти петлю, которая является явным признаком того, что что-то пошло не так. Это довольно редко, но это первый момент, потому что когда это происходит, это кошмар

2) Ожидание основного потока: если ваш основной поток находится в состоянии WAIT, проверьте, не поддерживается ли он другим потоком. Этого не должно быть, потому что ваш поток пользовательского интерфейса не должен удерживаться фоновым потоком.

Оба этих сценария означают, что вам необходимо значительно переработать код.

3) Тяжелые операции с основным потоком: это наиболее распространенная причина ANR, но иногда ее сложнее найти и исправить. Посмотрите на основные детали темы. Прокрутите трассировку стека и пока не увидите классы, которые вы узнаете (из вашего приложения). Посмотрите на методы в трассировке и выясните, выполняете ли вы сетевые вызовы, вызовы БД и т. Д. В этих местах.

Наконец, и я прошу прощения за бесстыдное подключение моего собственного кода, вы можете использовать анализатор журналов python, который я написал по адресу https://github.com/HarshEvilGeek/Android-Log-Analyzer. Это позволит вам просмотреть ваши файлы журналов, открыть файлы ANR, найти тупики, находите ожидающие основные потоки, находите неперехваченные исключения в журналах вашего агента и печатайте все это на экране относительно легко читаемым способом. Прочитайте файл ReadMe (который я собираюсь добавить), чтобы узнать, как его использовать. Это помогло мне за последнюю неделю!

Ахил Чериан Вергезе
источник
4

Всякий раз, когда вы анализируете проблемы синхронизации, отладка часто не помогает, поскольку остановка приложения в точке останова устранит проблему.

Лучше всего вставить множество журналов вызовов (Log.XXX ()) в различные потоки и обратные вызовы приложения и посмотреть, где задержка. Если вам нужна трассировка стека, создайте новое исключение (просто создайте его экземпляр) и зарегистрируйте его.

Ulrich
источник
2
Спасибо за совет по созданию нового исключения, если вам нужна трассировка стека. Это очень полезно при отладке :)
Кучи
3

Что запускает ANR?

Как правило, система отображает ANR, если приложение не может ответить на ввод пользователя.

В любой ситуации, когда ваше приложение выполняет потенциально длительную операцию, вы не должны выполнять работу над потоком пользовательского интерфейса, а вместо этого создавать рабочий поток и выполнять большую часть работы там. Это поддерживает работу потока пользовательского интерфейса (который управляет циклом событий пользовательского интерфейса) и предотвращает вывод системы о том, что ваш код заморожен.

Как избежать ANR

Приложения Android обычно работают полностью в одном потоке (по умолчанию «поток пользовательского интерфейса» или «основной поток»). Это означает, что все, что ваше приложение делает в потоке пользовательского интерфейса, выполнение которого занимает много времени, может вызвать диалог ANR, поскольку ваше приложение не дает себе возможности обрабатывать входное событие или намеренные широковещательные рассылки.

Поэтому любой метод, который выполняется в потоке пользовательского интерфейса, должен выполнять как можно меньше работы в этом потоке. В частности, действия должны делать как можно меньше для настройки в ключевых методах жизненного цикла, таких как 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, я написал небольшой скрипт на python, чтобы помочь исследовать ANR traces.txt.

ANR будут выводиться в виде графики, graphvizесли вы установили grapvhvizв вашей системе.

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

Png будет выводиться, как показано ниже, если в файле обнаружены ANR traces.txt. Это более интуитивно понятно.

введите описание изображения здесь

Пример traces.txtфайла, использованного выше, был получен отсюда .

alijandro
источник
0

Подумайте об использовании библиотеки ANR-Watchdog для точного отслеживания и захвата трассировок стека ANR с высоким уровнем детализации. Затем вы можете отправить их в свою библиотеку отчетов о сбоях. Я рекомендую использовать setReportMainThreadOnly()в этом сценарии. Вы можете либо заставить приложение генерировать нефатальное исключение точки замораживания, либо заставить приложение принудительно завершать работу, когда происходит ANR.

Обратите внимание, что стандартные отчеты ANR, отправляемые на вашу консоль разработчика Google Play, часто не достаточно точны, чтобы точно определить проблему. Вот почему нужна сторонняя библиотека.

Mr-IDE
источник