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

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

Спасибо.

Ответ 1

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

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

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

Ответ 2

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

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

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

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

Ответ 3

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

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

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

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

Основные шаги исследования

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

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

Ответ 4

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

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

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

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

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

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

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

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

Ответ 5

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

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

Ответ 6

Какие триггеры 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

Ответ 7

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

Ответ 9

Basic на @Horyun Lee ответ, я написал небольшой python script, чтобы помочь исследовать ANR из traces.txt.

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

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

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

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

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

Ответ 10

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

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