Android - как мне исследовать ANR? - PullRequest
142 голосов
/ 01 апреля 2009

Есть ли способ выяснить, где мое приложение выдало 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.

Спасибо.

Ответы [ 10 ]

117 голосов
/ 02 апреля 2009

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

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

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

93 голосов
/ 29 июля 2011

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

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

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

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

67 голосов
/ 15 июля 2014

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

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

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

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

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

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

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

11 голосов
/ 08 августа 2014

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

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

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

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

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

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

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

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

4 голосов
/ 18 февраля 2011

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

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

3 голосов
/ 25 апреля 2014

Что вызывает 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 голос
/ 18 мая 2017

Вам нужно найти «ожидание блокировки» в / data / anr / traces.txt file

enter image description here

для более подробной информации: Инженер для высокой производительности с инструментами от Android & Play (Google I / O '17)

1 голос
/ 06 февраля 2017

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

0 голосов
/ 07 августа 2017

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

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

0 голосов
/ 11 июля 2017

Основываясь на ответе @Horun Lee, я написал небольшой скрипт Python , чтобы помочь исследовать ANR с traces.txt.

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

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

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

enter image description here

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

...