Если поток UI приложения Android заблокирован слишком долго, возникает ошибка "Приложение не отвечает" (ANR). Если приложение работает в активном режиме, система показывает пользователю диалоговое окно, как на рисунке 1. В диалоговом окне ANR пользователю предлагается принудительно закрыть приложение.
Ошибки ANR возникают, когда основной поток приложения, отвечающий за обновление интерфейса, не может обработать действия пользователя или отрисовать экран. Это мешает работе с приложением. Подробнее об основном потоке приложения…
Ошибка ANR возникает в следующих случаях:
- Превышено время ожидания ввода. Приложение не ответило на событие ввода (например, нажатие клавиши или касание экрана) в течение пяти секунд.
- Выполнение сервиса. Если сервис, объявленный в приложении, не может завершить выполнение
Service.onCreateиService.onStartCommand/Service.onBindв течение нескольких секунд. - Метод
Service.startForegroundне вызывается. Приложение используетContext.startForegroundService, чтобы запустить новую активную службу, но она не вызывает методstartForegroundв течение пяти секунд. - Трансляция намерения. Если
BroadcastReceiverне завершает выполнение в течение заданного времени. Если приложение активно в фоновом режиме, время ожидания составляет 5 секунд. - Взаимодействия с
JobScheduler. ЕслиJobServiceне возвращается изJobService.onStartJobилиJobService.onStopJobв течение нескольких секунд или если запускается задача, инициированная пользователем, и ваше приложение не вызываетJobService.setNotificationв течение нескольких секунд после вызоваJobService.onStartJob. В приложениях, предназначенных для Android 13 и более ранних версий, ошибки ANR неявные и не регистрируются. В приложениях, предназначенных для Android 14 и более поздних версий, ошибки ANR явные и регистрируются.
Если в вашем приложении возникают ошибки ANR, воспользуйтесь рекомендациями из этой статьи, чтобы выявить и устранить проблему.
Диагностика ошибок ANR
При диагностике ошибок ANR следует обращать внимание на следующие распространенные проблемы:
- В основном потоке выполняются медленные операции ввода-вывода.
- Приложение выполняет длительные вычисления в основном потоке.
- Основной поток выполняет синхронный вызов механизма Binder к другому процессу, и этот процесс долго не возвращает результат.
- Основной поток заблокирован и ожидает синхронизированный блок для длительной операции, которая выполняется в другом потоке.
- Основной поток находится во взаимной блокировке с другим потоком в вашем процессе или через вызов Binder. Основной поток не просто ожидает завершения длительной операции, а находится в состоянии взаимной блокировки.
Определить причину ошибок ANR можно с помощью описанных ниже методов.
HealthStats
HealthStats предоставляет показатели работоспособности приложения, фиксируя общее время пользователя и системы, процессорное время, статистику сети и радио, время включения/выключения экрана и сигналы пробуждения. Это поможет вам измерить общее использование ЦП и
разряд батареи.
Отладка
Debug помогает проверять приложения для Android во время разработки, в том числе отслеживать и подсчитывать распределение ресурсов, чтобы выявлять задержки и зависания.
Вы также можете использовать Debug, чтобы получить счетчики памяти среды выполнения и нативной памяти, а также показатели памяти, которые помогут вам определить объем потребляемой памяти определенного процесса.
ApplicationExitInfo
ApplicationExitInfo доступен в Android 11 (уровень API 30) и более поздних версиях. Он предоставляет информацию о причине выхода из приложения. К ним относятся ошибки ANR, нехватка памяти, сбои в работе приложения, чрезмерная загрузка ЦП, прерывания со стороны пользователя и системы, а также изменения динамических разрешений.
Строгий режим
StrictMode помогает находить случайные операции I/O в основном потоке во время разработки приложения. StrictMode можно использовать на уровне приложения или действия.
Включить диалоговые окна ANR в фоновом режиме
Android показывает диалоговые окна ANR для приложений, которые слишком долго обрабатывают широковещательное сообщение, только если в настройках для разработчиков на устройстве включен параметр Показывать все ошибки ANR. Поэтому диалоговые окна ошибок ANR в фоновом режиме не всегда показываются пользователю, даже если в приложении возникают проблемы производительности.
Проблемы с рекомпозицией
Используйте Android Studio Profiler и инспектор макетов, чтобы выявить узкие места рекомпозиции. Подробнее о производительности Jetpack Compose…
Как получить файл трассировки
При возникновении ошибки ANR Android сохраняет информацию о трассировке. В более старых версиях ОС на устройстве есть только один файл /data/anr/traces.txt. В более новых версиях ОС файлов /data/anr/anr_* несколько. Вы можете получить доступ к трассировкам ANR с устройства или эмулятора, используя Android Debug Bridge (adb) в качестве корневого каталога:
adb root
adb shell ls /data/anr
adb pull /data/anr/<filename>
Чтобы получить отчет об ошибке с физического устройства, используйте параметр "Отчет об ошибке" в разделе "Для разработчиков" на устройстве или команду adb bugreport на компьютере для разработки. Подробнее о том, как создавать и читать отчеты об ошибках…
Устраните проблемы
Определив проблему, вы можете воспользоваться советами из этого раздела, чтобы устранить распространенные неполадки.
Медленный код в основном потоке
Определите, в каких местах кода основной поток приложения занят более пяти секунд. Попробуйте найти подозрительные сценарии использования в приложении и воспроизвести ошибку ANR.
Распространенная проблема – длительная задача непосредственно в composable-функции:
@Composable
fun BadList(rawStrings: List<String>) {
// Math or sorting inside the composable runs on EVERY recomposition pass!
val heavilyProcessedList = rawStrings
.filter { it.isNotBlank() }
.map { it.uppercase().reversed() }
.map { it.computationallyHeavyFunction() }
.sortedBy { it.length }
LazyColumn { items(sortedList) { Text(it) } }
}
// Modern Compose-first fix
@Composable
fun GoodList(viewModel: MyViewModel = viewModel()) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
// UI simply renders state; no heavy processing allowed here
LazyColumn { items(uiState.sortedData) { Text(it) } }
}
Операция ввода-вывода в основном потоке
Выполнение операций ввода-вывода в основном потоке – распространенная причина медленной работы основного потока, которая может приводить к ошибкам ANR. В Compose разработчики часто случайно запускают чтение с диска (например, SharedPreferences или вызовы базы данных), пытаясь получить начальное состояние.
Выполняйте длительные операции ввода-вывода отдельно от уровня пользовательского интерфейса. Используйте withContext(Dispatchers.IO) в ViewModel или, что ещё лучше, Repository на уровне данных.
Взаимные блокировки
Взаимная блокировка возникает, когда поток переходит в режим ожидания, потому что необходимый ресурс занят другим потоком, который также ожидает ресурс, занятый первым потоком. Если в такой ситуации оказывается основной поток приложения, скорее всего, возникнет ошибка ANR.
Взаимные блокировки – хорошо изученное явление в компьютерных науках, и существуют алгоритмы, которые позволяют их избежать.
Дополнительную информацию можно найти в статьях Википедии Взаимная блокировка и Алгоритмы предотвращения взаимной блокировки.
При использовании Kotlin и Compose вы можете заменить примитивные блокировки неблокирующими мьютексами сопрограмм (Mutex.withLock), чтобы предотвратить блокировку потока за счет приостановки контекста выполнения вместо замораживания потока пользовательского интерфейса. Пример:
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
// Modern non-blocking concurrency state architecture
class SecureDataRepository {
private val mutex = Mutex()
suspend fun safeUIAccess() {
// If locked, the main thread suspends seamlessly, preventing an ANR
mutex.withLock {
performSafeOperation()
}
}
}
Медленные широковещательные приемники
Приложения могут реагировать на широковещательные сообщения, например о включении или отключении режима полета или об изменении статуса подключения, с помощью широковещательных приемников. Если приложение обрабатывает широковещательное сообщение слишком долго, возникает ошибка ANR.
Ошибка ANR возникает в следующих случаях:
- Широковещательный приемник не завершил выполнение метода
onReceiveв течение значительного времени. - Широковещательный приемник вызывает
goAsync, но не вызываетfinishдля объектаPendingResult.
В методе onReceive класса BroadcastReceiver должны выполняться только короткие операции. Однако если вашему приложению требуется более сложная обработка в результате оповещения, вам следует отложить задачу до ViewModel (используя возможности сопрограмм, областей видимости и диспетчеров Kotlin), если ожидается, что задача займет не более нескольких секунд, любого типа хранилища состояния или до WorkManager для задач, которые, как ожидается, займут больше нескольких секунд.
GameActivity
Библиотека GameActivity снижает количество ошибок ANR в играх и приложениях, написанных на C или C++. Это подтверждено исследованиями. Если вы замените существующий класс native activity на GameActivity, то сможете снизить количество блокировок потока UI и предотвратить некоторые ошибки ANR.
Подробнее об ошибках ANR… Подробнее о повышении производительности за счет потоков…
Дополнительные ресурсы
Просмотр контента
Рекомендуем
- Примечание. Текст ссылки показывается, когда JavaScript отключен.
- Слишком частое пробуждение