Если поток интерфейса Android-приложения заблокирован слишком долго, система отправляет ошибку "Приложение не отвечает" (ANR). На этой странице описаны различные типы ошибок ANR, способы их диагностики и устранения. Все указанные диапазоны времени ожидания по умолчанию относятся к устройствам AOSP и Pixel. Устройства других производителей могут иметь другие значения.
При определении причины ошибок ANR полезно различать проблемы с системой и приложением.
Если система работает некорректно, ошибки ANR могут быть вызваны следующими проблемами:
- Временные проблемы с системным сервером приводят к тому, что быстрые вызовы Binder становятся медленными.
- Из-за проблем с системным сервером и высокой нагрузкой на устройство потоки приложений не планируются.
Если у вас есть такая возможность, определить, связана ли проблема с системой или приложением, можно с помощью трассировки Perfetto:
- Посмотрите, запланирован ли основной поток приложения, изучив трек состояния потока в Perfetto.
- Проверьте потоки
system_serverна наличие проблем, например конфликтов блокировок. - Если вы заметили, что вызовы связывателя выполняются медленно, проверьте цепочку ответов (если она есть), чтобы понять, почему это происходит.
Время ожидания отправки входных данных истекло
Ошибки ANR при обработке входных данных возникают, когда основной поток приложения не отвечает на событие ввода, например пролистывание или нажатие клавиши, в течение определенного времени. Поскольку приложение находится на переднем плане, когда происходит тайм-аут отправки входных данных, пользователь почти всегда видит это и важно устранить проблему.
Время ожидания по умолчанию: 5 секунд.
Ошибки ANR при обработке входных данных обычно связаны с проблемами в основном потоке. Если основной поток был заблокирован в ожидании получения блокировки, то поток, удерживающий блокировку, также может быть задействован.
Чтобы избежать ошибок ANR при отправке входных данных, следуйте рекомендациям ниже.
- Не выполняйте в основном потоке блокирующие или длительные операции. Попробуйте использовать
StrictMode, чтобы отслеживать случайные действия в основном потоке. - Минимизируйте конфликты блокировок между основным и другими потоками.
- Минимизируйте работу, не связанную с интерфейсом, в основном потоке, например при обработке трансляций или запуске сервисов.
Основные причины
Ниже перечислены распространенные причины ошибок ANR при обработке входных данных и способы их устранения.
| Причина | Алгоритмы работы | Предлагаемые исправления |
|---|---|---|
| Медленный вызов Binder | Основной поток выполняет длительный синхронный вызов механизма Binder. | Перенесите вызов из основного потока или попробуйте оптимизировать его, если вы являетесь владельцем API. |
| Многократные последовательные вызовы Binder | Основной поток выполняет много последовательных синхронных вызовов Binder. | Не выполняйте вызовы Binder в цикле. |
| Блокировка ввода-вывода | Основной поток выполняет блокирующий вызов ввода-вывода, например для доступа к базе данных или сети. | Переместите все блокирующие операции ввода-вывода из основного потока. |
| Конфликт блокировок | Основной поток заблокирован и ожидает получения блокировки. | Уменьшите конфликты блокировок между основным и другими потоками. Оптимизируйте медленный код в другом потоке. |
| Дорогой фрейм | В одном кадре выполняется слишком много операций отрисовки, из-за чего возникают сильные рывки. | Меньше работы по отрисовке кадра. Не используйте алгоритмы n2. Используйте эффективные компоненты для прокрутки и разбивки на страницы, например библиотеку разбивки на страницы Jetpack. |
| Заблокировано другим компонентом | Другой компонент, например широковещательный приемник, работает и блокирует основной поток. | По возможности перенесите все операции, не связанные с интерфейсом, из основного потока. Запускайте получателей широковещательных сообщений в отдельном потоке. |
| Зависание графического процессора | Зависание GPU – это системная или аппаратная проблема, из-за которой блокируется отрисовка и, следовательно, возникает ошибка ANR при обработке входных данных. | К сожалению, обычно исправить проблему на стороне приложения нельзя. Если возможно, обратитесь к команде по оборудованию, чтобы устранить неполадки. |
Как отлаживать
Начните отладку, посмотрев сигнатуру кластера ANR в Google Play Console или Firebase Crashlytics. Кластер обычно содержит верхние фреймы, которые, предположительно, вызвали ошибку ANR.
На приведенной ниже блок-схеме показано, как определить причину ошибки ANR при отправке данных о времени ожидания ввода.
Play Vitals может обнаруживать и помогать устранять некоторые из этих распространенных причин ANR. Например, если показатели Vitals обнаружили, что ошибка ANR произошла из-за конфликта блокировок, то в разделе Аналитика для ANR будет приведено описание проблемы и рекомендации по ее устранению.
Нет выбранного окна
События, такие как касания, отправляются непосредственно в нужное окно на основе проверки попадания, а события, такие как нажатия клавиш, требуют цели. Этот период называется окном фокусировки. На каждом экране может быть только одно активное окно, и обычно это окно, с которым в данный момент взаимодействует пользователь. Если активное окно не найдено, при вводе данных возникает ошибка ANR из-за отсутствия активного окна. Ошибка ANR без фокуса на окне относится к типу ошибок ANR при отправке входных данных.
Время ожидания по умолчанию: 5 секунд.
Основные причины
Ошибки ANR без фокуса обычно вызваны одной из следующих проблем:
- Приложение выполняет слишком много задач и не успевает отрисовать первый кадр.
- Основное окно не фокусируется. Если окно помечено как
FLAG_NOT_FOCUSABLE, пользователь не может отправлять в него события нажатия клавиш или кнопок.
Kotlin
override fun onCreate(savedInstanceState: Bundle) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) window.addFlags(WindowManager.LayoutParams.FLAG_FLAG_NOT_FOCUSABLE) }
Java
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); getWindow().addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE); }
Время ожидания широковещательного приемника истекло
Ошибка ANR широковещательного приемника возникает, когда широковещательный приемник не успевает обработать широковещательное сообщение. Для синхронных приемников или приемников, которые не вызывают goAsync(), время ожидания означает, что onReceive() не было выполнено вовремя. Для асинхронных получателей или получателей, которые вызывают goAsync(), истечение времени ожидания означает, что PendingResult.finish() не был вызван вовремя.
Ошибки ANR в широковещательном приемнике часто происходят в следующих потоках:
- Основной поток, если проблема связана с медленным запуском приложения.
- Поток, выполняющий широковещательный приемник, если проблема связана с медленным кодом
onReceive(). - Потоки трансляции, если проблема связана с медленным кодом трансляции
goAsync().
Чтобы избежать ошибок ANR в широковещательных приемниках, следуйте этим рекомендациям:
- Убедитесь, что приложение запускается быстро, поскольку время запуска учитывается в тайм-ауте ANR, если приложение запускается для обработки трансляции.
- Если используется
goAsync(), убедитесь, чтоPendingResult.finish()вызывается быстро. На него распространяется тот же тайм-аут ANR, что и на синхронные приемники широковещательных передач. - Если используется
goAsync(), убедитесь, что рабочий поток не используется для других длительных или блокирующих операций. - Чтобы не блокировать код интерфейса, выполняющийся в основном потоке, попробуйте использовать
registerReceiver()для запуска приемников широковещательных сообщений в потоке, отличном от основного.
Периоды ожидания
Время ожидания получения широковещательного сообщения зависит от того, установлен ли флаг намерения переднего плана и от версии платформы.
| Тип намерения | Android 13 и более ранние версии | Android 14 и более поздних версий |
|---|---|---|
Намерение с приоритетом активного режима ( |
10 секунд; |
10–20 секунд в зависимости от того, насколько загружен процессор |
Намерение, связанное с приоритетом в фоновом режиме ( |
60 секунд |
60–120 секунд в зависимости от того, насколько загружен ЦП. |
Чтобы узнать, установлен ли флаг FLAG_RECEIVER_FOREGROUND, найдите в теме ANR строку "flg=" и проверьте, есть ли в ней значение 0x10000000. Если этот бит установлен, то для намерения задано значение FLAG_RECEIVER_FOREGROUND, поэтому время ожидания меньше.
Пример темы ошибки ANR с коротким временем ожидания трансляции (10–20 секунд):
Broadcast of Intent { act=android.inent.action.SCREEN_ON flg=0x50200010 }
Пример темы ошибки ANR с длительным временем ожидания трансляции (60–120 секунд):
Broadcast of Intent { act=android.intent.action.TIME_SET flg=0x25200010 }
Как измеряется время трансляции
Измерение продолжительности трансляции начинается, когда трансляция отправляется из system_server в приложение, и заканчивается, когда приложение завершает обработку трансляции. Если процесс приложения не был запущен, он должен выполнить холодный запуск в течение периода ожидания ANR. Поэтому медленный запуск приложения может привести к ошибкам ANR в широковещательном приемнике.
На рисунке ниже показано, как временная шкала ANR для широковещательного приемника соотносится с определенными процессами приложения.
Время ожидания ANR заканчивается, когда получатель завершает обработку трансляции. Это зависит от того, является ли получатель синхронным или асинхронным.
- Для синхронных получателей измерение прекращается, когда возвращается значение
onReceive(). - Для асинхронных получателей измерение прекращается при вызове
PendingResult.finish().
Основные причины
Ниже перечислены распространенные причины ошибок ANR в широковещательных приемниках и способы их устранения.
| Причина | Область применения | Что произошло | Предлагаемое решение |
|---|---|---|---|
| Медленный запуск приложения | Все получатели | Холодный запуск приложения занял слишком много времени. | Оптимизируйте медленный запуск приложения. |
onReceive(): не запланировано | Все получатели | Поток широковещательного приемника был занят другой работой и не мог запустить метод onReceive(). | Не выполняйте длительные задачи в потоке получателя (или перенесите получателя в отдельный поток). |
Устройство "onReceive()" работает медленно | Все получатели, но в основном синхронные | Метод onReceive() был запущен, но был заблокирован или работал слишком медленно, поэтому не завершился вовремя. | Оптимизируйте код получателя. |
| Асинхронные задачи приемника не запланированы | goAsync()
ресиверы; | Метод onReceive() попытался выполнить работу
в заблокированном пуле рабочих потоков, поэтому работа не началась. |
Оптимизируйте медленные или блокирующие вызовы или используйте разные потоки для рабочих процессов трансляции и других длительных задач. |
| Рабочие процессы выполняются медленно или заблокированы | goAsync() ресиверов |
При обработке трансляции в пуле рабочих потоков произошла блокировка или медленная операция. Поэтому PendingResult.finish
не был вызван вовремя. | Оптимизируйте медленный код получателя async. |
Забыли позвонить PendingResult.finish |
goAsync() ресиверов |
Вызов функции finish() отсутствует в пути выполнения кода. |
Убедитесь, что finish() вызывается всегда. |
Как отлаживать
На основе сигнатуры кластера и отчета ANR можно найти поток, в котором выполняется получатель, а затем и конкретный код, который отсутствует или работает медленно.
На схеме ниже показано, как определить причину ошибки ANR, связанной с приемником широковещательных передач.
Как найти код ресивера
В Google Play Console в сигнатуре ошибки ANR указаны класс получателя и широковещательное намерение. Обратите внимание на следующее:
cmp=<receiver class>act=<broadcast_intent>
Вот пример сигнатуры ANR для приемника широковещательных сообщений:
com.example.app.MyClass.myMethod
Broadcast of Intent { act=android.accounts.LOGIN_ACCOUNTS_CHANGED
cmp=com.example.app/com.example.app.MyAccountReceiver }
Как найти поток, в котором выполняется метод onReceive()
Если вы используете Context.registerReceiver, чтобы указать обработчик, это поток, в котором выполняется этот обработчик. В противном случае это основная цепочка.
Пример: асинхронные задачи получателя не запланированы
В этом разделе приведен пример того, как отлаживать ошибку ANR в широковещательном приемнике.
Предположим, сигнатура ошибки ANR выглядит следующим образом:
com.example.app.MyClass.myMethod
Broadcast of Intent {
act=android.accounts.LOG_ACCOUNTS_CHANGED cmp=com.example.app/com.example.app.MyReceiver }
Судя по подписи, намерение трансляции – android.accounts.LOG_ACCOUNTS_CHANGED, а класс получателя – com.example.app.MyReceiver.
Из кода получателя можно определить, что основной объем работы по обработке этой трансляции выполняет пул потоков "BG Thread
[0,1,2,3]". Анализ дампов стека показывает, что все четыре фоновых потока имеют одинаковую структуру: они выполняют блокирующий вызов getDataSync. Поскольку все фоновые потоки были заняты, широковещательное сообщение не удалось обработать вовремя, что привело к ошибке ANR.
BG Thread #0 (tid=26) Waiting
at jdk.internal.misc.Unsafe.park(Native method:0)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:211)
at com.google.common.util.concurrent.AbstractFuture.get(AbstractFuture:563)
at com.google.common.util.concurrent.ForwardingFuture.get(ForwardingFuture:68)
at com.example.app.getDataSync(<MyClass>:152)
...
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:644)
at com.google.android.libraries.concurrent.AndroidExecutorsModule.lambda$withStrictMode$5(AndroidExecutorsModule:451)
at com.google.android.libraries.concurrent.AndroidExecutorsModule$$ExternalSyntheticLambda8.run(AndroidExecutorsModule:1)
at java.lang.Thread.run(Thread.java:1012)
at com.google.android.libraries.concurrent.ManagedPriorityThread.run(ManagedPriorityThread:34)
There are several approaches to fix the issue:
- Find out why
getDataSyncis slow and optimize. - Don't run
getDataSyncon all four BG threads. - More generally, ensure that the BG thread pool isn't saturated with long-running operations.
- Use a dedicated thread pool for
goAsyncworker tasks. - Use an unbounded thread pool instead of the bounded BG thread pool
Example: slow app startup
A slow app startup can cause several types of ANRs, especially broadcast
receiver and execute service ANRs. The cause of an
ANR is likely slow app startup if you see ActivityThread.handleBindApplication
in the main thread stacks.
Execute service timeout
An execute service ANR happens when the app's main thread doesn't start a
service in time. Specifically, a service doesn't finish executing
onCreate() and onStartCommand() or onBind() within the
timeout period.
Default timeout period: 20 seconds for foreground service; 200 seconds for
background service. The ANR timeout period includes the app cold start, if
necessary, and calls to onCreate(), onBind(), or onStartCommand().
To avoid execute service ANRs, follow these general best practices:
- Make sure that app startup is fast, since it's counted in the ANR timeout if the app is started to run the service component.
- Make sure that the service's
onCreate(),onStartCommand(), andonBind()methods are fast. - Avoid running any slow or blocking operations on the main thread from other components; these operations can prevent a service from starting quickly.
Common causes
The following table lists common causes of execute service ANRs and suggested fixes.
| Cause | What | Suggested fix |
|---|---|---|
| Slow app startup | The app takes too long to perform a cold start. | Optimize slow app start. |
Slow onCreate(), onStartCommand(), or
onBind() |
The service component's onCreate(),
onStartCommand(), or onBind() method takes too long to
execute on the main thread. |
Optimize slow code. Move slow operations off the critical path where possible. |
Not scheduled (main thread blocked before onStart()) |
The app's main thread is blocked by another component before the service can be started. | Move other component's work off the main thread. Optimize other component's blocking code. |
How to debug
From the cluster signature and ANR report in Google Play Console or Firebase Crashlytics, you can often determine the cause of the ANR based on what the main thread is doing.
The following flow chart describes how to debug an execute service ANR.
If you've determined that the execute service ANR is actionable, follow these steps to help resolve the issue:
Find the service component class in the ANR signature. In Google Play Console, the service component class is shown in the ANR signature. In the following example ANR details, it's
com.example.app/MyService.com.google.common.util.concurrent.Uninterruptibles.awaitUninterruptibly Executing service com.example.app/com.example.app.MyServiceDetermine whether the slow or block operation is part of app startup, the service component, or elsewhere by checking for the following important function call(s) in the main threads.
Function call(s) in main thread stacks What it means android.app.ActivityThread.handleBindApplicationApp was starting up, so the ANR was caused by slow app start. <ServiceClass>.onCreate()
[...]
android.app.ActivityThread.handleCreateService
Service was being created, so the ANR was likely caused by slow onCreate()code.<ServiceClass>.onBind()
[...]
android.app.ActivityThread.handleBindService
Service was being bound, so the ANR was likely caused by slow onBind()code.<ServiceClass>.onStartCommand()
[...]
android.app.ActivityThread.handleServiceArgs
Service was being started, so the ANR was likely caused by slow onStartCommand()code.For example, if the
onStartCommand()method in theMyServiceclass is slow, the main threads will look like this:at com.example.app.MyService.onStartCommand(FooService.java:25) at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:4820) at android.app.ActivityThread.-$$Nest$mhandleServiceArgs(unavailable:0) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2289) at android.os.Handler.dispatchMessage(Handler.java:106) at android.os.Looper.loopOnce(Looper.java:205) at android.os.Looper.loop(Looper.java:294) at android.app.ActivityThread.main(ActivityThread.java:8176) at java.lang.reflect.Method.invoke(Native method:0)Если вы не видите ни одного важного вызова функции, возможны следующие варианты:
- Сервис работает или завершает работу, а значит, стеки были получены слишком поздно. В этом случае ошибку ANR можно проигнорировать как ложноположительный результат.
- Запущен другой компонент приложения, например широковещательный приемник. В этом случае основной поток, скорее всего, заблокирован в этом компоненте, что не позволяет запустить сервис.
Если вы видите вызов ключевой функции и можете определить, где именно происходит ошибка ANR, проверьте остальные стеки основного потока, чтобы найти медленную операцию, оптимизировать ее или перенести ее за пределы критического пути.
- Убедитесь, что приложение запускается быстро, поскольку время запуска учитывается в тайм-ауте ANR, если приложение запускается для работы с поставщиком контента.
- Убедитесь, что запросы к поставщику контента выполняются быстро.
- Не выполняйте много параллельных вызовов блокировки связывателя, которые могут заблокировать все потоки связывателя приложения.
- Незаметные зависания интерфейса. Интерфейс приложения перестает реагировать на действия пользователя.
- Сбои. В приложении может произойти сбой с ошибкой
illegalStateException. - Ошибки ANR. Система может вызвать тайм-аут ANR.
- Планирование обходов.
ViewRootImplпланирует обход (проход макета и отрисовки), отправляя барьер синхронизации вMessageQueueпотока интерфейса. Этот барьер приостанавливает обычную обработку сообщений, чтобы приоритет был у макета интерфейса. - Состояние гонки. Когда несколько потоков пытаются одновременно сделать представление недействительным, они конкурируют за планирование этого обхода. Оба потока могут успешно вставить барьер синхронизации, но фреймворк сохранит токен только для одного из них.
- Зависание интерфейса. При выполнении обхода удаляется только один сохраненный барьер. Вторичные, "протекающие" барьеры остаются в очереди на неопределенный срок, навсегда блокируя поток UI от обработки любых синхронных сообщений.
- Взаимодействуйте только с объектами
View, в том числе считывайте свойства, такие какwidthилиheight, или задавайте свойства, напримерTextView's text, в том потоке, в котором вы создали иерархию View. Обычно это основной поток или поток интерфейса. - Если вы не можете гарантировать, что при доступе к представлениям вы работаете в потоке UI, считайте, что это небезопасно. Например, если вы используете
Coroutinesдля фоновой работы, перед манипуляцией объектамиViewнеобходимо переключиться на основной диспетчер. ContextCompat#getMainExecutor(android.content.Context)View.post(Runnable)Activity.runOnUiThread(Runnable)- Установите отладочную версию приложения на устройство с Android 17 или более поздней версии.
- Откройте приложение "Настройки" на устройстве и выберите Система > Дополнительно > Для разработчиков > Изменения совместимости приложений.
- Выберите приложение из списка.
- В списке изменений найдите переключатель
ENFORCE_THREAD_CHECKS_ON_VIEW_ROOT_IMPL_APISи включите его. - Телеметрия и отслеживание. Этот прослушиватель позволяет приложению получать обратные вызовы, когда API View вызывается из неправильного потока. Это упрощает ведение журнала телеметрии и отслеживание ошибок.
- Встроенное выполнение. Прослушиватель вызывается встроенным образом, что позволяет вам получить и проверить точную трассировку стека в момент нарушения.
- Проблема на уровне системы. Процесс не был запланирован из-за высокой нагрузки на систему или проблемы с системным сервером.
- Поздний дамп стека. Поток восстановился за короткий период между запуском ANR и дампом стека. Задержка на устройствах Pixel с Android 13 составляет около 100 мс, но может превышать 1 с. На устройствах Pixel с Android 14 задержка обычно составляет менее 10 мс.
- Неправильное указание автора цепочки. Поток, использованный для создания сигнатуры ANR, не был потоком, который перестал отвечать и вызвал ANR.
- Посмотреть нарушение цепочки. Если ваше приложение изменяет представление в фоновом потоке, это может вызвать состояние гонки во внутренних процессах представления, из-за чего задачи потока UI будут заблокированы.
- Высокая нагрузка на систему. Оцените общую нагрузку на ресурсы устройства, например нехватку системного процессора, памяти или ресурсов ввода-вывода, как основную причину отсутствия ответа.
- Неправильное распределение потоков. Проверьте рабочие и связующие потоки на наличие взаимоблокировок, конфликтов блокировок, влияющих на основной поток, или зависающих фоновых потоков, обрабатывающих асинхронные компоненты (например,
goAsync()). - Проверка нарушений потоков. Сканирование фоновых потоков на предмет незаконных изменений иерархии представлений пользовательского интерфейса, которые могут привести к потере барьера синхронизации в MessageQueue и постоянной блокировке всех синхронных сообщений.
- Сбор стека занимает слишком много времени и завершается с ошибкой.
- Процесс завершился или был завершен до того, как были получены стеки.
Подробнее о сервисах:
Поставщик контента не отвечает
Ошибка ANR поставщика контента возникает, когда удаленный поставщик контента отвечает на запрос дольше, чем предусмотрено тайм-аутом, и завершает работу.
Период ожидания по умолчанию задается поставщиком контента с помощью тега ContentProviderClient.setDetectNotResponding. Время ожидания ANR включает общее время выполнения запроса к удаленному поставщику контента, в том числе холодный запуск удаленного приложения, если оно ещё не было запущено.
Чтобы избежать ошибок ANR в поставщике контента, следуйте этим рекомендациям:
Основные причины
В таблице ниже перечислены распространенные причины ошибок ANR, связанных с поставщиком контента, и предложены способы их устранения.
| Причина | Алгоритмы работы | Сигнал | Предлагаемое решение |
|---|---|---|---|
| Медленный запрос к поставщику контента | Поставщик контента слишком долго выполняет запрос или заблокирован. | Фрейм android.content.ContentProvider$Transport.query находится в потоке связывания. |
Оптимизируйте запрос поставщика контента. Узнайте, что блокирует поток binder. |
| Медленный запуск приложения | Приложение поставщика контента запускается слишком долго. | Фрейм ActivityThread.handleBindApplication находится в основном потоке. |
Оптимизируйте запуск приложения. |
| Исчерпание потоков Binder – все потоки Binder заняты | Все потоки связывания заняты обработкой других синхронных запросов, поэтому вызов связывания поставщика контента не может быть выполнен. | Приложение не запускается, все потоки Binder заняты, а поставщик контента не работает. | Снижение нагрузки на потоки связывания. То есть совершать меньше синхронных исходящих вызовов Binder или выполнять меньше работы при обработке входящих вызовов. |
Как отлаживать
Чтобы отладить ошибку ANR поставщика контента, используя сигнатуру кластера и отчеты по ANR в Google Play Console или Firebase Crashlytics, посмотрите, что делают основной поток и потоки связывания.
На схеме ниже показано, как отлаживать ошибки ANR поставщика контента.
В приведенном ниже фрагменте кода показано, как выглядит поток связывания, когда он заблокирован из-за медленного запроса к поставщику контента. В этом случае запрос поставщика контента ожидает блокировки при открытии базы данных.
binder:11300_2 (tid=13) Blocked
Waiting for osm (0x01ab5df9) held by at com.google.common.base.Suppliers$NonSerializableMemoizingSupplier.get(Suppliers:182)
at com.example.app.MyClass.blockingGetOpenDatabase(FooClass:171)
[...]
at com.example.app.MyContentProvider.query(MyContentProvider.java:915)
at android.content.ContentProvider$Transport.query(ContentProvider.java:292)
at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:107)
at android.os.Binder.execTransactInternal(Binder.java:1339)
at android.os.Binder.execTransact(Binder.java:1275)
В приведенном ниже фрагменте кода показано, как выглядит основной поток, когда он заблокирован из-за медленного запуска приложения. В этом случае приложение запускается медленно из-за конфликта блокировок при инициализации Dagger.
main (tid=1) Blocked
[...]
at dagger.internal.DoubleCheck.get(DoubleCheck:51)
- locked 0x0e33cd2c (a qsn)at dagger.internal.SetFactory.get(SetFactory:126)
at com.myapp.Bar_Factory.get(Bar_Factory:38)
[...]
at com.example.app.MyApplication.onCreate(DocsApplication:203)
at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1316)
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:6991)
at android.app.ActivityThread.-$$Nest$mhandleBindApplication(unavailable:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2235)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:205)
at android.os.Looper.loop(Looper.java:294)
at android.app.ActivityThread.main(ActivityThread.java:8170)
at java.lang.reflect.Method.invoke(Native method:0)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:971)
Медленный ответ на задание
Ошибка ANR при медленном ответе задания возникает, когда приложению требуется слишком много времени, чтобы ответить на запрос JobService.onStartJob() или JobService.onStopJob() или чтобы предоставить уведомление с помощью JobService.setNotification(). Это означает, что основной поток приложения заблокирован при выполнении другой задачи.
Если проблема связана с JobService.onStartJob() или JobService.onStopJob(), проверьте, что происходит в основном потоке. Если проблема связана с JobService.setNotification(), позвоните как можно скорее.
Не выполняйте много работы до отправки уведомления.
Как посмотреть нарушение правил в отношении цепочек писем
Если ваше приложение изменяет представление в фоновом потоке, это может вызвать состояние гонки во внутреннем состоянии иерархии представлений. Это нарушение может заблокировать основной поток (интерфейс) от выполнения любых синхронных сообщений, что приведет к серьезным проблемам со стабильностью:
Это нарушение работы с потоками является одной из основных причин ошибок ANR, в стеке вызовов которых основной поток бездействует (например, захвачен в nativePollOnce или main thread idle).
Утечка барьера синхронизации
Представления можно сделать недействительными косвенно, изменив их состояние (например, вызвав метод TextView.setText или View.setVisibility), или напрямую, вызвав метод View.invalidate или View.requestLayout. Когда представление становится недействительным,
ViewRootImpl планирует обход для выполнения измерений, макета и отрисовки, чтобы обновить состояние интерфейса.
В результате интерфейс зависает. Поскольку MessageQueue не может обработать синхронные сообщения после утечки барьеров, основной поток переходит в состояние ожидания. Обычно эта проблема проявляется как ошибка ANR с nativePollOnce в трассировке стека.
Чтобы избежать нарушений, связанных с потоками представлений, следуйте приведенным ниже рекомендациям.
lifecycleOwner.lifecycleScope.launch(Dispatchers.IO) { val data = myRepository.getData() withContext(Dispatchers.Main) { // Switch context to Main myTextView.text = data.title } }
Чтобы вы могли следовать рекомендациям, Android предлагает несколько способов доступа к потоку интерфейса из других потоков:
Популярные загрузчики изображений, фреймворки для реактивных расширений, библиотеки для шины событий и другие решения для работы с потоками обычно предлагают способы отслеживать определенные события в основном потоке. Это полезно для наблюдателей и прослушивателей событий, которым необходимо получать и задавать состояние представлений.
Как отлаживать
В Android 17 появились новые инструменты, которые помогают выявлять, отслеживать и устранять нарушения потоков представлений во время разработки.
1. Инструменты фреймворка совместимости
Инструменты фреймворка совместимости позволяют разработчикам приложений включать и отключать изменения в поведении по отдельности с помощью параметров разработчика или ADB. Чтобы включить или отключить изменения в поведении при нарушении потоков просмотра, выполните следующие действия:
Вы также можете включить или отключить флаг с помощью ADB:
$ adb shell am compat enable ENFORCE_THREAD_CHECKS_ON_VIEW_ROOT_IMPL_APIS <your.package.name>
$ adb shell am compat disable ENFORCE_THREAD_CHECKS_ON_VIEW_ROOT_IMPL_APIS <your.package.name>
Если включить этот флаг, приложение будет вызывать исключение и аварийно завершать работу при каждом вызове API View из неправильного потока. Это поможет вам выявлять и устранять проблемы с потоками View.
2. CalledFromWrongThreadListener API
Вы также можете реализовать глобальный API View#registerCalledFromWrongThreadListener, чтобы программно обнаруживать неправильный доступ к потоку.
android {
//...
compileSdk = 37
}
val listener = object : View.CalledFromWrongThreadListener { override fun onCalledFromWrongThread() { // Handle the issue, e.g. crash if this is a dev build, or log an event // e.g. Log.d(TAG, "CalledFromWrongThread: ${Exception().stackTraceToString()}") // Unregister the listener to avoid redundant notifications for the same issue View.unregisterCalledFromWrongThreadListener(this) } } View.registerCalledFromWrongThreadListener(listener)
nativePollOnce
Если в стеках ANR вы видите фрейм nativePollOnce или message queue idle, это часто означает, что подозреваемый поток на самом деле был неактивен и ожидал сообщений от Looper. В Google Play Console сведения об ошибках ANR выглядят так:
Native method - android.os.MessageQueue.nativePollOnce
Executing service com.example.app/com.example.app.MyService
Например, если основной поток неактивен, стеки выглядят следующим образом:
"main" tid=1 NativeMain threadIdle
#00 pc 0x00000000000d8b38 /apex/com.android.runtime/lib64/bionic/libc.so (__epoll_pwait+8)
#01 pc 0x0000000000019d88 /system/lib64/libutils.so (android::Looper::pollInner(int)+184)
#02 pc 0x0000000000019c68 /system/lib64/libutils.so (android::Looper::pollOnce(int, int*, int*, void**)+112)
#03 pc 0x000000000011409c /system/lib64/libandroid_runtime.so (android::android_os_MessageQueue_nativePollOnce(_JNIEnv*, _jobject*, long, int)+44)
at android.os.MessageQueue.nativePollOnce (Native method)
at android.os.MessageQueue.next (MessageQueue.java:339) at android.os.Looper.loop (Looper.java:208)
at android.app.ActivityThread.main (ActivityThread.java:8192)
at java.lang.reflect.Method.invoke (Native method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:626)
at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1015)
Подозреваемый поток может быть неактивным по нескольким причинам:
Поскольку причины ошибок ANR типа nativePollOnce зависят от категории ANR, диагностика поможет определить, какие действия нужно предпринять, чтобы устранить проблему в приложении.
| Категория | Причина | Предлагаемое решение |
|---|---|---|
| Время ожидания отправки входных данных истекло | Проблема в системе Поздний дамп стека |
Действий не требуется. |
| Нет выбранного окна | Системная проблема Поздний дамп стека Нарушение потоков |
Проверьте базу кода на наличие нарушений потоков представлений. |
| Время ожидания широковещательного приемника истекло | Поздний дамп стека Проблема на уровне системы Неправильное отнесение к потоку Посмотреть нарушение потоков |
Проверьте нужные потоки в дампе стека. Проверьте базу кода на наличие нарушений потоков представлений. |
| Время ожидания ответа от сервиса истекло | Поздний дамп стека Проблема на уровне системы Нарушение потоков |
Проверьте базу кода на наличие нарушений потоков представлений. |
| Поставщик контента не отвечает | Поздний дамп стека Проблема на уровне системы Неправильное отнесение к потоку |
Проверьте нужные потоки в дампе стека. |
Вот как анализировать ошибки ANR в nativePollOnce:
Нет стековых фреймов
В некоторых отчетах по ANR нет стеков с ANR. Это означает, что при создании отчета по ANR не удалось выполнить дамп стека. Отсутствие кадров стека может быть вызвано двумя причинами:
[...]
--- CriticalEventLog ---
capacity: 20
timestamp_ms: 1666030897753
window_ms: 300000
libdebuggerd_client: failed to read status response from tombstoned: timeout reached?
----- Waiting Channels: pid 7068 at 2022-10-18 02:21:37.<US_SOCIAL_SECURITY_NUMBER>+0800 -----
[...]
Ошибки ANR без фреймов стека нельзя устранить, используя сигнатуру кластера или отчет ANR. Чтобы отладить приложение, посмотрите другие кластеры, поскольку если проблема достаточно серьезная, то у нее обычно есть собственный кластер, в котором присутствуют кадры стека. Также можно посмотреть трассировки Perfetto.
Известные проблемы
Использование таймера в процессе приложения для завершения обработки широковещательного сообщения до того, как будет активирована ошибка ANR, может работать неправильно из-за асинхронного способа, которым система отслеживает ошибки ANR.