Ошибки anr что это

Время на прочтение
4 мин

Количество просмотров 26K

ANR (Application Not Responding) — ошибка, которая возникает, когда приложение не отвечает. В итоге открывается диалоговое окно, предлагающее пользователю подождать или закрыть приложение.
image alt

Условия возникновения ANR

  • Входные события (кнопки и сенсорные события) не обрабатываются в течение 5 секунд;
  • BroadcastReceiver (onRecieve()) не был обработан в течение указанного времени (foreground — 10 с, background — 60 с);
  • ContentProvider не завершен в течение 10 секунд.

Обычно основной поток блокируется.

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

Класс AppErrors занимается обработкой не только ANR, но и других ошибок, которые могут возникнуть в приложении, включая crash. Метод handleShowAnrUi() как раз и открывает это страшное для многих разработчиков и пользователей окно, отображающее ANR.

class AppErrors {
    ...
    
    void handleShowAnrUi(Message msg) {
        Dialog dialogToShow = null;
        synchronized (mService) {
            AppNotRespondingDialog.Data data = (AppNotRespondingDialog.Data) msg.obj;
            final ProcessRecord proc = data.proc;
            if (proc == null) {
                Slog.e(TAG, "handleShowAnrUi: proc is null");
                return;
            }
            if (proc.anrDialog != null) {
                Slog.e(TAG, "App already has anr dialog: " + proc);
                MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_APP_ANR,
                        AppNotRespondingDialog.ALREADY_SHOWING);
                return;
            }

            Intent intent = new Intent("android.intent.action.ANR");
            if (!mService.mProcessesReady) {
                intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
                        | Intent.FLAG_RECEIVER_FOREGROUND);
            }
            mService.broadcastIntentLocked(null, null, intent,
                    null, null, 0, null, null, null, AppOpsManager.OP_NONE,
                    null, false, false, MY_PID, Process.SYSTEM_UID, 0 /* TODO: Verify */);

            boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(),
                    Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;
            if (mService.canShowErrorDialogs() || showBackground) {
                dialogToShow = new AppNotRespondingDialog(mService, mContext, data);
                proc.anrDialog = dialogToShow;
            } else {
                MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_APP_ANR,
                        AppNotRespondingDialog.CANT_SHOW);
                // Just kill the app if there is no dialog to be shown.
                mService.killAppAtUsersRequest(proc, null);
            }
        }
        // If we've created a crash dialog, show it without the lock held
        if (dialogToShow != null) {
            dialogToShow.show();
        }
    }

...

Однако, ANR начинается не здесь. Как я и говорила выше, одна из первых причин возникновения этой ошибки — это задержка входного события, которая составляет 5 секунд. Недолгим поиском мы можем найти где задается это значение.

namespace android {
// Default input dispatching timeout if there is no focused application or paused window
// from which to determine an appropriate dispatching timeout.
const nsecs_t DEFAULT_INPUT_DISPATCHING_TIMEOUT = 5000 * 1000000LL; // 5 sec

Теперь мы можем посмотреть в коде, где вызывается нативная часть. Это происходит в классе InputManagerService.

    // Native callback.
    private long notifyANR(InputApplicationHandle inputApplicationHandle,
            InputWindowHandle inputWindowHandle, String reason) {
        return mWindowManagerCallbacks.notifyANR(
                inputApplicationHandle, inputWindowHandle, reason);
    }

А вот и mWindowManagerCallbacks в InputMonitor:

        if (appWindowToken != null && appWindowToken.appToken != null) {
            // Notify the activity manager about the timeout and let it decide whether
            // to abort dispatching or keep waiting.
            final AppWindowContainerController controller = appWindowToken.getController();
            final boolean abort = controller != null
                    && controller.keyDispatchingTimedOut(reason,
                            (windowState != null) ? windowState.mSession.mPid : -1);
            if (!abort) {
                // The activity manager declined to abort dispatching.
                // Wait a bit longer and timeout again later.
                return appWindowToken.mInputDispatchingTimeoutNanos;
            }
        } else if (windowState != null) {
            try {
                // Notify the activity manager about the timeout and let it decide whether
                // to abort dispatching or keep waiting.
                long timeout = ActivityManager.getService().inputDispatchingTimedOut(
                        windowState.mSession.mPid, aboveSystem, reason);
                if (timeout >= 0) {
                    // The activity manager declined to abort dispatching.
                    // Wait a bit longer and timeout again later.
                    return timeout * 1000000L; // nanoseconds
                }
            } catch (RemoteException ex) {
            }
        }
        return 0; // abort dispatching
    }

Давайте посмотрим внимательнее на inputDispatchingTimedOut(). Тут как раз мы и показываем сообщение через ActivityManager об истечении времени ожидания и даем пользователю решить, следует ли отменить действие или продолжить ожидание. И именно в ActivityManagerService и вызывается AppErrors в случае возникновения crash или ANR.

  private boolean makeAppCrashingLocked(ProcessRecord app,
            String shortMsg, String longMsg, String stackTrace) {
        app.crashing = true;
        app.crashingReport = generateProcessError(app,
                ActivityManager.ProcessErrorStateInfo.CRASHED, null, shortMsg, longMsg, stackTrace);
        startAppProblemLocked(app);
        app.stopFreezingAllLocked();
        return handleAppCrashLocked(app, shortMsg, longMsg, stackTrace);
    }
    private void makeAppNotRespondingLocked(ProcessRecord app,
            String activity, String shortMsg, String longMsg) {
        app.notResponding = true;
        app.notRespondingReport = generateProcessError(app,
                ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING,
                activity, shortMsg, longMsg, null);
        startAppProblemLocked(app);
        app.stopFreezingAllLocked();
    }

Основные причины ANR

  • Блокировка ввода/вывода
  • Перегрузка сети
  • Блокировка потоков
  • Бесконечный цикл
  • Бизнес-логика выполняется слишком долго

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

  • Главный поток пользовательского интерфейса выполняет логику, связанную только с пользовательским интерфейсом;
  • Сложные вычисления (например, операции с базой данных, операции ввода-вывода, сетевые операции и т.д.) производятся в отдельном потоке;
  • Используйте Handler для взаимодействия между потоком пользовательского интерфейса и рабочим потоком;
  • Используйте RxJava и т.д. для обработки асинхронных операций.

Как поймать ANR

  • Информация об ANR может храниться в файле /data/anr/traces.txt, либо по другому пути /data/anr/anr_*. Получить ее можно с помощью следующих команд:
    adb root
    adb shell ls /data/anr
    adb pull /data/anr/<filename>
  • Использовать проект с открытым исходным кодом ANR-WatchDog для обнаружения ANR
  • См. Как избежать ANR :)

P.S. Все подборки я публикую в телеграм канале @paradisecurity.

ANR stands for Application Not Responding. An ANR will occur if you’re running a process on the UI thread which takes an extended time, usually around 5 seconds. During this point, the GUI (Graphical User Interface) will lock up which can end in anything the user presses won’t be actioned. After the 5 seconds approx. has occurred, if the thread still hasn’t recovered then an ANR dialogue box is shown informing the user that the appliance isn’t responding and can give the user the choice to either wait, in the hope that the app will eventually recover, or to force close the app. This is how the ANR dialog displayed to the user.

ANR dialog displayed to the user

A crash is when an exception within the app has been thrown which has not been handled. For example, if you are trying to set the text of an EditText component, but the EditText is null and there’s no try-catch statement to catch the exception then your app will crash and can be force closed. The user won’t see what caused the crash, they’re going to be shown a dialogue telling that the app has force closed unexpectedly and can give them the choice to send a bug report. For example, an error caused by java.lang.NullPointerException is an ANR. An ANR happens when some long operation takes place in the “main” thread. This is the event loop thread, and if it’s busy, Android cannot process any longer GUI events within the application, and thus presents an ANR dialog. Now, within the trace you posted, most thread seems to be doing fine, there’s no problem. It is idling within the MessageQueue, expecting another message to return. In your case, the ANR was likely a longer operation, rather than something that blocked the thread permanently, so the event thread recovered after the operation finished, and your trace went through after the ANR.

How to prevent an ANR?

Stop doing heavy tasks on the main thread. Instead use worker threads such as IntentService, AsyncTask Handler, or another Thread simply. Detecting where ANRs happen is straightforward if it’s a permanent block (deadlock acquiring some locks for instance), but harder if it’s just a short-lived delay. First, re-evaluate your code and appearance for vulnerable spots and long-running operations. Examples may include using sockets, locks, thread sleeps, and other blocking operations from within the event thread. You should make sure these all happen in separate threads. If nothing seems the matter, use DDMS and enable the thread view. This shows all the threads in your application similar to the trace you have. Reproduce the ANR, and refresh the most thread at an equivalent time. That should show you exactly what’s happening at the time of the ANR. An ANR is going to be triggered for your app when one among the subsequent conditions occur:

  1. While your activity is within the foreground, your app has not fed with an input event or BroadcastReceiver (such as key press or screen touch events) within 5 seconds.
  2. While you aren’t doing an activity within the foreground, your BroadcastReceiver hasn’t finished executing within a substantial amount of your time.

If your app is experiencing ANRs, you’ll use the guidance during this article to diagnose and fix the matter.

Android vitals

Android vitals can help improve your app’s performance by alerting you, via the Play Console, when your app is exhibiting excessive ANRs. Android vitals considers ANRs excessive when an app:

  1. Exhibits a minimum of one ANR in a minimum of 0.47% of its daily sessions.
  2. Exhibits 2 or more ANRs in a minimum of 0.24% of its daily sessions.
  3. Exhibits 3 or more ANRs in a minimum of 0.17% of its daily sessions.
  4. Exhibits 3 or more ANRs in a minimum of 0.14% of its daily sessions.

Diagnosing ANRs

The ANRs could be solved easily as the Android Interface Provide Efficient Tools to remove them! Finding them may include (but are not limited to):

  1. The app is doing slow operations which involves the I/O operation of the operating system.
  2. The main thread is doing a synchronous binder call to a different process, which another process is taking an extended time to return.
  3. The main thread is during a deadlock with another thread, either in your process or via a binder call. the most thread isn’t just expecting an extended operation to end but is during a deadlock situation.
  4. The app is doing an extended calculation on the basic thread.

Using the Android Strict Mode (API Level > 9)

Starting API Level 9 using StrictMode will help you discover certain accidental I/O operations on the most thread while you’re developing your app. you’ll use StrictMode at the appliance or activity level. Read more about how to use StrictMode here.

Moreover, you can also find about the ANRs by pulling a Traces File from Android ADB as Follows:

adb root
adb shell ls /data/anr
adb pull /data/anr/<filename>

But if your users are still experiencing ANRs, you are bound to check out the status of the prime a.k.a. main thread in Android Device Monitor. Usually, the main thread is in the RUNNABLE state if it’s ready to update the UI and is generally responsive if everything is fine!

An Example

Java

@Override

public void onClick(View view) {

    MergeSort.sort(data);

}

In the above-mentioned case what we need to do is to remove the sorting process from the main thread and place it into another class or thread, so that the system can handle it asynchronously.

A Tip

When an ANR occurs and your app crashes out of nowhere, Android Studio logs some info related to the case in a text file on the device itself. You can use ADB to gather logs and have a look. You can also add Crashlytics from Firebase to catch hold of ANRs when they occur in your app, and get their frequency, and event the activity thread on which it happened! View More Details about ANR by visiting Here.

Last Updated :
06 Feb, 2023

Like Article

Save Article

Приложение отвечает: как мы уменьшили количество ANR-ошибок в шесть раз. Часть 1, про сбор данных +12

Разработка под Android, Тестирование мобильных приложений, Аналитика мобильных приложений, Блог компании Badoo, Google API


Рекомендация: подборка платных и бесплатных курсов 3D-моделирования — https://katalog-kursov.ru/

Пожалуй, одна из худших проблем, которая может случиться с вашим приложением, — ошибка ANR (Application Not Responding), когда приложение не отвечает. Если таких ошибок много, они могут негативно влиять не только на пользовательский опыт, но и на позицию в выдаче Google Play и фичеринг. 

В начале прошлого года количество ANRs в приложении Badoo превышало порог “Bad Behaviour” в Google Play. Поэтому мы собрали команду для решения этой проблемы и потратили несколько месяцев, экспериментируя с разными подходами. В результате мы смогли уменьшить количество таких ошибок более чем в шесть раз.

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

В первой части мы поговорим об основах: что представляет собой ошибка ANR и как её лучше отслеживать. Если вы уже знакомы с этой темой, предлагаю перейти ко второй части, в которой я расскажу о наших способах решения этой проблемы.

Что такое ошибка ANR?

Обычно любое приложение с графическим интерфейсом выполняет все связанные с ним операции и отрисовку в отдельном UI-потоке исполнения. Android не исключение: здесь в главном потоке приложения выполняется цикл, отвечающий за все действия с интерфейсом:

При использовании этого цикла крайне важно не выполнять длительные операции, потому что это напрямую повлияет на отзывчивость приложения. Если в главном потоке выполнять слишком много действий, это может привести к снижению частоты кадров или даже к зависаниям интерфейса:

Чтобы как-то идентифицировать такие ситуации, в Android ввели понятие ANR, с помощью которого система сообщает, что приложение зависло. Вот что об этом говорится в официальной документации:

Когда UI-поток Android-приложения блокируется слишком долго, выдаётся ошибка Application Not Responding (ANR).

ANR выдаётся, когда приложение находится в одном из этих состояний:

— на переднем плане находится Activity, приложение в течение пяти секунд не отвечает на входящие события или BroadcastReceiver, например нажатия на кнопки или касания экрана;

— на переднем плане нет Activity, ваш BroadcastReceiver не закончил исполнение в течение длительного времени.

Если ANR случается, когда на переднем плане находится Activity вашего приложения, Android показывает диалоговое окно с предложением закрыть приложение или подождать.

Довольно легко принудительно вызвать ANR, написав Thread.sleep() в любом обработчике интерфейса, например обработчик нажатия кнопки. После нажатия на кнопку вы увидите примерно следующее:

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

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

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

Отслеживание ANR

Локальный анализ

Самый простой случай — если у вас есть возможность стабильно воспроизводить ANR-проблему локально. Существует довольно много инструментов, которые могут помочь вам быстро найти источник проблемы.

Первое, что можно сделать, — это проверить дамп стек-трейсов для всех потоков (thread dump). Когда приложение перестает отвечать, Android создаёт дамп всех текущих потоков, который может помочь в анализе проблемы. Обычно он находится в директории /data/anr/, точный путь можно найти в Logcat сразу после сообщения об ошибке ANR.

Дамп потоков содержит стек-трейсы: вы увидите, в каком состоянии был каждый поток (например, какая строка выполнялась в конкретный момент времени). По сути, это состояние приложения на момент создания дампа.

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

Отслеживание с помощью Google Play

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

Во-первых, можно увидеть агрегированные графики с общим количеством ANR-ошибок за день. Также есть такая метрика, как ANR rate — отношение количества сессий за день, в которых возникала хотя бы одна ANR-ошибка, к общему количеству сессий за сутки. Для этой метрики задан порог в 0,47%, превышение которого считается «неудовлетворительным поведением» (“Bad Behaviour”) и может плохо повлиять на позицию приложения в Google Play. 

Во-вторых, можно открывать отдельные отчёты об ANR-ошибках, сгруппированные по схожести на основе стек-трейса. Основные группы находятся в разделе Android Vitals. И это, вероятно, наиболее полезный раздел для выявления самых частых причин возникновения ANR-ошибок в вашем приложении.

Если вы активно используете консоль Google Play, вы могли заметить некоторые её недостатки. Например, к отчётам нельзя прикрепить дополнительную информацию, такую как логи для отладки. Также невозможно настроить логику группировки отчётов. Иногда система помещает в одну группу ошибки, возникшие по разным причинам, а иногда раскидывает по разным группам ошибки, у которых причина одна.

Всё это иногда затрудняет определение основных ошибок и поиск изначальных проблем. Что же можно сделать для улучшения ситуации?

Скачивание данных из Google Play

Для решения проблемы с логикой группировки можно попробовать скачать сырые отчёты об ANR-ошибках из Google Play для последующего ручного анализа. Раньше была возможность выгрузить эти данные из Google Cloud Storage, но несколько лет назад Google перестала поддерживать этот функционал:

Однако всё ещё можно просматривать отдельные отчёты в консоли. Но как нам экспортировать тысячи отчётов, не потратив при этом кучу времени на рутинную работу?

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

Веб-скрапер можно реализовать с помощью популярного инструмента Selenium, который предоставляет простой интерфейс для взаимодействия с веб-страницами. Изначально он предназначался для создания автоматизированных тестов для веб-приложений и доступен на разных языках, включая Java и Kotlin.

Мы реализовали скрапер на Selenium и получили сырые отчёты об ANR-ошибках для одного из релизов. Благодаря этому нам удалось проанализировать их так, как не получилось бы сделать с помощью встроенных в консоль Google Play инструментов. Например, просто поискав в отчётах по ключевым словам “Application.onCreate”, мы обнаружили, что около 60% ошибок произошло во время выполнения метода Application.onCreate. При этом в консоли Google Play нет возможности получить такую информацию, так как отчёты разбиты по группам.

Внутренняя аналитика

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

Его функциональность схожа с возможностями других инструментов для краш-репортинга, таких как Firebase Crashlytics и App Center, но ещё и позволяет нам полностью контролировать сохраняемые данные, менять логику группировки и применять сложную фильтрацию:

Это не реальные данные приложения Bumble, иллюстрация сделана просто для примера

Это не реальные данные приложения Bumble, иллюстрация сделана просто для примера

Мы решили отслеживать в Gelato ещё и ANR-ошибки в надежде, что это поможет нам в поиске их причин. Для этого нам нужно было знать, когда приложение перестаёт отвечать. В Android 11 появился новый API, предоставляющий информацию о недавних причинах завершения процесса, но у большинства наших пользователей установлены более ранние версии ОС, поэтому нам требовалось найти другое решение.

И мы нашли простой способ, который часто используется для отслеживания зависаний главного потока исполнения: запустить watchdog-поток, который периодически будет пытаться выполнить задачу в главном потоке. Если задача не выполняется за определённый промежуток времени, то можно сохранить дамп текущего состояния потоков и отправить его в наш инструмент для анализа отчётов о падениях:

Такую логику реализует, например, библиотека, которой мы воспользовались для реализации репортинга в Gelato. Это позволило нам проводить более глубокий анализ данных и лучше интегрировать этот инструмент в нашу инфраструктуру. Например, теперь мы можем сравнивать зависания главного потока в разных вариантах в ходе A/B-тестирования.

Вот пример отчёта в нашей системе:

Это не реальные данные приложения Bumble, иллюстрация сделана просто для примера

Это не реальные данные приложения Bumble, иллюстрация сделана просто для примера

Полезный совет: собирайте и отправляйте вместе с отчётом лог событий аналитики. Иногда это даёт возможность буквально пошагово воспроизвести проблему. 

Если у вас нет своего решения для сбора отчётов о падениях приложения, вы можете настроить репортинг и в сторонние инструменты. Например, можно отправлять ANR-ошибки в App Center или Firebase Crashlytics, так как они предоставляют API для отправки кастомных крашей.

Но помните, что все эти отчёты нельзя считать полной альтернативой ANR-отчётам в Google Play (как мы говорили выше, в Android немного другие правила определения таких ошибок). Но в любом случае это может помочь получить общее представление об основных проблемах. Вполне вероятно, что если генерируется много отчётов о зависании главного потока исполнения в какой-то части вашего приложения, то в ней происходят и ANR-ошибки. 

В завершение

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

Ссылки

  1. Android Vitals в Google Play

  2. Отладка ANR

  3. API для получения причин завершения процесса

  4. Фреймворк для тестирования веб-страниц

  5. Библиотека для определения зависаний

Пожалуй, одна из худших проблем, которая может случиться с вашим приложением, — ошибка ANR (Application Not Responding), когда приложение не отвечает. Если таких ошибок много, они могут негативно влиять не только на пользовательский опыт, но и на позицию в выдаче Google Play и фичеринг. 

В начале прошлого года количество ANRs в приложении Badoo превышало порог “Bad Behaviour” в Google Play. Поэтому мы собрали команду для решения этой проблемы и потратили несколько месяцев, экспериментируя с разными подходами. В результате мы смогли уменьшить количество таких ошибок более чем в шесть раз.

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

В первой части мы поговорим об основах: что представляет собой ошибка ANR и как её лучше отслеживать. Если вы уже знакомы с этой темой, предлагаю подождать выхода второй части, в которой я расскажу о наших способах решения этой проблемы.

Что такое ошибка ANR?

Обычно любое приложение с графическим интерфейсом выполняет все связанные с ним операции и отрисовку в отдельном UI-потоке исполнения. Android не исключение: здесь в главном потоке приложения выполняется цикл, отвечающий за все действия с интерфейсом:

При использовании этого цикла крайне важно не выполнять длительные операции, потому что это напрямую повлияет на отзывчивость приложения. Если в главном потоке выполнять слишком много действий, это может привести к снижению частоты кадров или даже к зависаниям интерфейса:

Чтобы как-то идентифицировать такие ситуации, в Android ввели понятие ANR, с помощью которого система сообщает, что приложение зависло. Вот что об этом говорится в официальной документации:

Когда UI-поток Android-приложения блокируется слишком долго, выдаётся ошибка Application Not Responding (ANR).

ANR выдаётся, когда приложение находится в одном из этих состояний:

— на переднем плане находится Activity, приложение в течение пяти секунд не отвечает на входящие события или BroadcastReceiver, например нажатия на кнопки или касания экрана;

— на переднем плане нет Activity, ваш BroadcastReceiver не закончил исполнение в течение длительного времени.

Если ANR случается, когда на переднем плане находится Activity вашего приложения, Android показывает диалоговое окно с предложением закрыть приложение или подождать.

Довольно легко принудительно вызвать ANR, написав Thread.sleep() в любом обработчике интерфейса, например обработчик нажатия кнопки. После нажатия на кнопку вы увидите примерно следующее:

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

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

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

Отслеживание ANR

Локальный анализ

Самый простой случай — если у вас есть возможность стабильно воспроизводить ANR-проблему локально. Существует довольно много инструментов, которые могут помочь вам быстро найти источник проблемы.

Первое, что можно сделать, — это проверить дамп стек-трейсов для всех потоков (thread dump). Когда приложение перестает отвечать, Android создаёт дамп всех текущих потоков, который может помочь в анализе проблемы. Обычно он находится в директории /data/anr/, точный путь можно найти в Logcat сразу после сообщения об ошибке ANR.

Дамп потоков содержит стек-трейсы: вы увидите, в каком состоянии был каждый поток (например, какая строка выполнялась в конкретный момент времени). По сути, это состояние приложения на момент создания дампа.

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

Отслеживание с помощью Google Play

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

Во-первых, можно увидеть агрегированные графики с общим количеством ANR-ошибок за день. Также есть такая метрика, как ANR rate — отношение количества сессий за день, в которых возникала хотя бы одна ANR-ошибка, к общему количеству сессий за сутки. Для этой метрики задан порог в 0,47%, превышение которого считается «неудовлетворительным поведением» (“Bad Behaviour”) и может плохо повлиять на позицию приложения в Google Play. 

Во-вторых, можно открывать отдельные отчёты об ANR-ошибках, сгруппированные по схожести на основе стек-трейса. Основные группы находятся в разделе Android Vitals. И это, вероятно, наиболее полезный раздел для выявления самых частых причин возникновения ANR-ошибок в вашем приложении.

Если вы активно используете консоль Google Play, вы могли заметить некоторые недостатки. Например, к отчётам нельзя прикрепить дополнительную информацию, такую как логи для отладки. Также невозможно настроить логику группировки отчётов. Иногда система помещает в одну группу ошибки, возникшие по разным причинам, а иногда раскидывает по разным группам ошибки, у которых причина одна.

Всё это иногда затрудняет определение основных ошибок и поиск изначальных проблем. Что же можно сделать для улучшения ситуации?

Скачивание данных из Google Play

Для решения проблемы с логикой группировки можно попробовать скачать сырые отчёты об ANR-ошибках из Google Play для последующего ручного анализа. Раньше была возможность выгрузить эти данные из Google Cloud Storage, но несколько лет назад Google перестала поддерживать этот функционал:

Однако всё ещё можно просматривать отдельные отчёты в консоли. Но как нам экспортировать тысячи отчётов, не потратив при этом кучу времени на рутинную работу?

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

Веб-скрапер можно реализовать с помощью популярного инструмента Selenium, который предоставляет простой интерфейс для взаимодействия с веб-страницами. Изначально он предназначался для создания автоматизированных тестов для веб-приложений и доступен на разных языках, включая Java и Kotlin.

Мы реализовали скрапер на Selenium и получили сырые отчёты об ANR-ошибках для одного из релизов. Благодаря этому нам удалось проанализировать их так, как не получилось бы сделать с помощью встроенных в консоль Google Play инструментов. Например, просто поискав в отчётах по ключевым словам “Application.onCreate”, мы обнаружили, что около 60% ошибок произошло во время выполнения метода Application.onCreate. При этом в консоли Google Play нет возможности получить такую информацию, так как отчёты разбиты по группам.

Внутренняя аналитика

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

Его функциональность схожа с возможностями других инструментов для краш-репортинга, таких как Firebase Crashlytics и App Center, но ещё и позволяет нам полностью контролировать сохраняемые данные, менять логику группировки и применять сложную фильтрацию:

Это не реальные данные приложения Bumble, иллюстрация сделана просто для примера

Это не реальные данные приложения Bumble, иллюстрация сделана просто для примера

Мы решили отслеживать в Gelato ещё и ANR-ошибки в надежде, что это поможет нам в поиске их причин. Для этого нам нужно было знать, когда приложение перестаёт отвечать. В Android 11 появился новый API, предоставляющий информацию о недавних причинах завершения процесса, но у большинства наших пользователей установлены более ранние версии ОС, поэтому нам требовалось найти другое решение.

И мы нашли простой способ, который часто используется для отслеживания зависаний главного потока исполнения: запустить watchdog-поток, который периодически будет пытаться выполнить задачу в главном потоке. Если задача не выполняется за определённый промежуток времени, то можно сохранить дамп текущего состояния потоков и отправить его в наш инструмент для анализа отчётов о падениях:

Такую логику реализует, например, библиотека, которой мы воспользовались для реализации репортинга в Gelato. Это позволило нам проводить более глубокий анализ данных и лучше интегрировать этот инструмент в нашу инфраструктуру. Например, теперь мы можем сравнивать зависания главного потока в разных вариантах в ходе A/B-тестирования.

Вот пример отчёта в нашей системе:

Это не реальные данные приложения Bumble, иллюстрация сделана просто для примера

Это не реальные данные приложения Bumble, иллюстрация сделана просто для примера

Полезный совет: собирайте и отправляйте вместе с отчётом лог событий аналитики. Иногда это даёт возможность буквально пошагово воспроизвести проблему. 

Если у вас нет своего решения для сбора отчётов о падениях приложения, вы можете настроить репортинг и в сторонние инструменты. Например, можно отправлять ANR-ошибки в App Center или Firebase Crashlytics, так как они предоставляют API для отправки кастомных крашей.

Но помните, что все эти отчёты нельзя считать полной альтернативой ANR-отчётам в Google Play (как мы говорили выше, в Android немного другие правила определения таких ошибок). Но в любом случае это может помочь получить общее представление об основных проблемах. Вполне вероятно, что если генерируется много отчётов о зависании главного потока исполнения в какой-то части вашего приложения, то в ней происходят и ANR-ошибки. 

В завершение

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

Ссылки

  1. Android Vitals в Google Play: https://developer.android.com/distribute/best-practices/develop/android-vitals

  2. Отладка ANR: https://developer.android.com/topic/performance/vitals/anr

  3. API для получения причин завершения процесса: https://developer.android.com/reference/kotlin/android/app/ActivityManager#gethistoricalprocessexitreasons

  4. Фреймворк для тестирования веб-страниц: https://www.selenium.dev/

  5. Библиотека для определения зависаний: https://github.com/SalomonBrys/ANR-WatchDog

На чтение 2 мин. Просмотров 136 Опубликовано

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

Содержание

  1. Что вызывает ANR?
  2. Распространенные виновники
  3. Методы диагностики
  4. Использовать строгий режим
  5. Включить фоновые диалоги ANR в параметрах разработчика
  6. Проверяйте файлы трассировки с помощью adb

Что вызывает ANR?

  1. Если ваше приложение не отвечает на ввод пользователя или BroadcastReceiver в течение пяти секунд.

  2. Ваш BroadcastReceiver не завершил выполнение в течение значительного промежутка времени, и в приложении нет текущей задачи переднего плана.

задача переднего плана — это задача, заметная пользователю. Примером текущей задачи переднего плана является музыкальное приложение, воспроизводящее звук. Эти задачи отображают уведомление для пользователя.

** BroadcastReceiver — это компонент, который прослушивает общесистемные трансляции. например, трансляция с сообщением об отключении экрана.

Распространенные виновники

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

  1. Медленные операции с I/ O в основном потоке.
  2. Выполнение длительных вычислений в основном потоке.
  3. Выполнение синхронного вызова связывателя в основном потоке, а другой процесс требует времени, чтобы ответить .
  4. Ожидание в основном потоке другого потока, выполняющего длительную операцию.
  5. Застрял в тупиковой ситуации. Взаимоблокировки могут возникать в основном потоке, когда он ожидает ресурсов, которые не были освобождены другим процессом, поэтому он не может получить необходимые ему ресурсы. Эти другие процессы могут быть в другом потоке, вашем процессе или в вызове связывателя.

Методы диагностики

Использовать строгий режим

С помощью StrictMode вы можете обнаруживать случайные операции ввода-вывода.

Подробнее о StrictMode в официальной документации.

Включить фоновые диалоги ANR в параметрах разработчика

Не все ANR видны пользователю; следовательно, приложение может столкнуться с проблемами производительности без ведома пользователя. Включите фоновые диалоги ANR в параметрах разработчика .

Проверяйте файлы трассировки с помощью adb

Файлы трассировки создаются в событии ANR. Вы можете получить эти файлы, используя Android Debug Bridge (adb) как root, используя следующие команды:

  adb rootadb shell ls/data/anradb pull/data/anr/  

Прочтите полную документацию по Android для ANR здесь.

  • Справка
  • Центр правил

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

Как найти данные

  1. Откройте Play Console.
  2. Выберите приложение.
  3. В меню слева выберите Качество > Android Vitals > Сбои и ошибки ANR.
  4. Используйте фильтры в центре экрана для поиска и диагностики неполадок. Чтобы получить подробную информацию о сбое или ошибке ANR, нажмите Подробнее () рядом с нужной строкой.

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

Сбои

На этой странице представлены сбои, которые влияют на показатель «Доля сбоев». Сбои группируются по кластерам, чтобы легче было выявлять общие причины.

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

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

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

Примечание. Доля сбоев в активном режиме – один из основных показателей Android Vitals, то есть важнейших технических показателей. Он влияет на параметры видимости приложения в Google Play.

Сбои, связанные с SDK

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

Если у сбоя есть отметка «Потенциально связано с SDK» на обзорной странице Android Vitals (Качество > Android Vitals > Общие сведения) и вам кажется, что он вызван неполадкой в коде SDK, вы можете поделиться информацией с поставщиком SDK, чтобы быстрее решить проблему. Отчет о сбое будет содержать название приложения, полную трассировку стека и другие сопутствующие сведения. Поставщики SDK могут просматривать информацию о сбоях, которой вы поделились, в Google Play SDK Console.

Они также могут давать разработчикам приложений рекомендации по устранению сбоев, связанных с их SDK, в разделе «Примечания от поставщика SDK» на обзорной странице Android Vitals (Качество > Android Vitals > Общие сведения). Предоставленная информация поможет выявить основную причину проблемы и способы решения. При появлении примечания к сбою, затронувшему приложение, вам придет уведомление об этом в разделе входящих сообщений в Play Console.

Вы можете помочь нам понять, полезно ли примечание, нажав на значок «Нравится» или «Не нравится» внизу страницы.

Узнать больше о доступных SDK можно на сайте Google Play SDK Index.

Ошибки ANR («Приложение не отвечает»)

Когда приложение не отвечает, открывается диалоговое окно, предлагающее пользователю подождать или закрыть приложение. Такие проблемы называются ошибками ANR. Сведения об ошибках ANR на устройствах с Android 10 и более ранними версиями доступны только в Play Console.

На этой странице представлены ошибки ANR, которые влияют на показатель «Доля ошибок ANR». Ошибки ANR группируются по кластерам, чтобы легче было выявлять общие причины.

Если вы хотите посмотреть кластеры для ошибок ANR в активном режиме, используйте фильтр Видимость проблемы.

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

Рекомендации по диагностике и устранению ошибок ANR можно найти на сайте для разработчиков Android.

Примечание. Доля сбоев в активном режиме – один из основных показателей Android Vitals, то есть важнейших технических показателей. Он влияет на параметры видимости приложения в Google Play.

Материалы по теме

Узнать больше об обзорной странице Android Vitals можно в статье о том, как отслеживать производительность приложения с помощью Android Vitals.

Эта информация оказалась полезной?

Как можно улучшить эту статью?

Понравилась статья? Поделить с друзьями:
  • Ошибки ebs volvo
  • Ошибки bmw e39 drive2 ru перевод
  • Ошибки caterpillar c13
  • Ошибки bmw 116i на приборной панели
  • Ошибки alfa romeo 159