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.

Дякую.


2
У мене є один подібний звіт, який також трапляється на android.os.MessageQueue.nativePollOnce(Native Method). Чи можна сміливо це ігнорувати?
rds

Відповіді:


124

ANR трапляється, коли деяка тривала операція відбувається в "головному" потоці. Це потік циклу подій, і якщо він зайнятий, Android не може обробляти подальші події GUI у додатку, і таким чином виводить діалогове вікно ANR.

Тепер у простежуваному вами трасі, здається, основна тема працює добре, немає жодних проблем. Він працює в режимі очікування в черзі повідомлень повідомлень, очікуючи, що надійде ще одне повідомлення. У вашому випадку ANR, швидше за все, триваліша операція, а не щось, що постійно блокувало потік, тому подія події відновилася після завершення операції, і ваш слід пройшов через після ANR.

Виявити, де трапляються ANR, легко, якщо це постійний блок (наприклад, тупик, який отримує деякі блоки), але складніше, якщо це лише тимчасова затримка. Спочатку перегляньте свій код і шукайте вразливі місця та тривалі операції. Приклади можуть включати використання сокетів, замків, сплячих потоків та інших операцій блокування з потоку події. Ви повинні переконатися, що все це відбувається в окремих потоках. Якщо нічого не здається проблемою, використовуйте DDMS та ввімкніть перегляд потоку. Це показує всі потоки у вашій програмі аналогічні тому, який ви маєте. Відтворіть ANR та оновіть основну нитку одночасно. Це має точно показати вам, що відбувається під час ANR


6
єдине питання - "відтворити ANR" :-). Ви можете, будь ласка, пояснити, як головна тема "простоює" у цій програмі простеження слідів шоу, це було б чудово.
Blundell

20
Трасування стека показує, що головний потік знаходиться в Looper (реалізація циклу повідомлень) і робить тимчасове очікування через Object.wait. Це означає, що в циклі повідомлень наразі немає жодного повідомлення для відправки, і він чекає, коли нові повідомлення надійдуть. ANR трапляється, коли система реалізує, що цикл повідомлень витрачає багато часу на обробку повідомлення, а не на обробку інших повідомлень у черга. Якщо петлі чекають повідомлень, очевидно, це не відбувається.
sooniln

3
@Soonil Привіт, ти знаєш, що решта розділів означає, наприклад, Пов'язка 3, Пов’язка нитки 2 JDWP демон пріо 5. Що таке sCount, dsCount, obj, sysTid, приємно заплановані засоби. також у ньому є така інформація, як VMWAIT, RUNNABLE, NATIVE
minhaz

1
Моя програма заснована на NDK, я бачу той самий ANR. Крім того, основна нитка є тонкою. Я спробував DDMS та оновив свою робочу нитку, коли вона замерзне. На жаль, я отримую лише рядок NativeStart :: run. Чи здатний перегляд потоку DDMS навіть перевіряти рідні потоки NDK? Також: StrictMode нічого не знайшов.
Брам

6
Див. Elliotth.blogspot.com/2012/08/…, щоб отримати гарне пояснення результатів.
sooniln

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) ... для запобігання включенню у виробництво
Amir Uval

@uval, що ви маєте на увазі під "запобіганням включення у виробництво"? !!
Мухаммед Рефаат

2
@MuhammedRefaat це не запобігає ANR. Це додаток вийде з ладу відразу, а не через 5 секунд. Наприклад, якщо ви отримаєте доступ до бази даних в основному потоці, і це займе 2 секунди, ви не отримаєте ANR, але StrictMode перестане програму. StrictMode призначений виключно для вашої налагоджувальної фази, а не для виробництва.
Амір Уваль

1
@MuhammedRefaat додав мою відповідь на ваше запитання.
Амір Уваль

80

Вам цікаво, у якому завданні міститься нитка інтерфейсу користувача. Файл слідів дає вам підказку, щоб знайти завдання. вам потрібно дослідити стан кожної нитки

Стан нитки

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

Зосередьтеся на СУСПАННІ, штаті МОНІТОР. Стан монітора вказує, який потік досліджується, і СУПРЕНДЕНИЙ стан потоку, ймовірно, є основною причиною тупикової ситуації.

Основні етапи дослідження

  1. Знайдіть "очікування блокування"
    • Ви можете знайти стан монітора "Швидка в'язка №15" prio = 5 tid = 75 MONITOR
    • вам пощастило, якщо знайдете "чекаюче заблокувати"
    • приклад: очікування блокування <0xblahblah> (a com.foo.A) утримується threadid = 74
  2. Ви можете помітити, що "tid = 74" зараз виконує завдання. Тож переходьте до tid = 74
  3. tid = 74, можливо, СУСПАНДОВА держава! знайдіть головну причину!

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


1
Приємне пояснення. Тепер мені простіше зрозуміти журнали ANR. Але я все ще маю проблему зрозуміти причину, тому що на кроці 1 я можу легко знайти ідентифікатор потоку, але коли на кроці 2 я намагаюся піти куди він, щоб перевірити стан, я не можу його знайти . Будь-яка ідея, як діяти з цим?
THZ

1
Я маю - waiting to lock an unknown objectвсередині "HeapTaskDaemon" daemon prio=5 tid=8 Blocked . Що це означає, чи може хтось допомогти?
Хілал

13

Останні кілька місяців я вивчав андроїд, тому далеко не експерт, але дуже розчарувався в документації про ANR.

Більшість порад, здається, спрямовані на те, щоб уникнути їх чи виправити, сліпо переглядаючи ваш код, що чудово, але я не міг знайти нічого, аналізуючи слід.

Є три речі, які вам дійсно потрібно шукати з журналами ANR.

1) Тупики: Коли потік знаходиться в стані ЗАЧЕКАЙТЕ, ви можете переглянути деталі, щоб дізнатися, хто це "holdby =". Більшу частину часу він буде триматися сам, але якщо він тримається іншою ниткою, це, ймовірно, буде знаком небезпеки. Подивіться на цю нитку і подивіться, на чому вона тримається. Ви можете знайти цикл, що є явним знаком того, що щось пішло не так. Це досить рідко, але це перший пункт, тому що коли це трапляється, це кошмар

2) Основна нитка Очікування: Якщо ваша основна нитка перебуває в режимі ЗАЧЕКАЙТЕ, перевірте, чи вона містить інший потік. Це не повинно відбуватися, тому що ваш потік інтерфейсу не повинен утримуватися фоновим потоком.

Обидва ці сценарії означають, що вам потрібно значно переробити код.

3) Важкі операції на головній нитці: Це найчастіша причина виникнення АНР, але іноді одна з найскладніших для пошуку та виправлення. Подивіться на основні деталі нитки. Прокрутіть вниз слід стека і доки не побачите класи, які ви впізнаєте (зі свого додатка). Погляньте на методи, які простежуються, і з’ясуйте, чи здійснюєте ви в цьому місці мережеві дзвінки, db-дзвінки тощо.

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


4

Щоразу, коли ви аналізуєте проблеми з тимчасовим відладкою, налагодження часто не допомагає, оскільки заморожування програми на точці перерви призведе до усунення проблеми.

Ваша найкраща ставка - вставити безліч журнальних дзвінків (Log.XXX ()) у різні потоки та зворотні дзвінки програми та побачити, де знаходиться затримка. Якщо вам потрібна стек-трек, створіть новий виняток (просто інстанціюйте) та введіть його.


2
Дякуємо за пораду щодо створення нового винятку, якщо вам потрібен стек-трек. Це дуже корисно при налагодженні :)
kuchi

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 ...


що надзвичайно дивно
Нілабджа


0

Основна відповідь на @Horyun Lee, я написав невеликий сценарій python, щоб допомогти дослідити ANR з traces.txt.

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

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

Png виведе, як нижче, якщо у файлі виявлено ANR traces.txt. Це більш інтуїтивно зрозуміло.

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

Зразок traces.txtфайлу, який використовується вище, отриманий звідси .


0

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

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.