Что такое ANR?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
ANR (Application Not Responding) — зависание приложения
ANR — это критическая ошибка, которая возникает, когда приложение перестаёт отвечать на действия пользователя. Система показывает диалог с вопросом «Приложение не отвечает. Закрыть или подождать?» Это одна из основных причин для выставления низких оценок приложению в Play Store.
Когда возникает ANR?
Андроид имеет watchdog таймер для каждого приложения. Если UI поток занят дольше определённого времени, система показывает ANR:
- 5 секунд — для обработки события (click, touch, back button)
- 10 секунд — для BroadcastReceiver
- 60 секунд — для Service
// ❌ Плохо — заблокирует UI на 5+ секунд → ANR
button.setOnClickListener {
val data = loadDataFromDatabase() // долгая операция
textView.text = data
}
// ✅ Хорошо — используем корутину
button.setOnClickListener {
viewModelScope.launch {
val data = withContext(Dispatchers.IO) {
loadDataFromDatabase()
}
textView.text = data
}
}
Главная причина ANR — блокирующие операции на главном потоке
1. Синхронные операции с БД
// ❌ ANR — синхронный запрос к БД на главном потоке
class UserFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val user = database.getUserSync(userId) // БЛОКИРУЕТ
textView.text = user.name
}
}
// ✅ Асинхронно через Repository
class UserViewModel : ViewModel() {
val user: StateFlow<User> = repository.getUser(userId)
.stateIn(viewModelScope, SharingStarted.Lazily, User())
}
2. Сетевые запросы на главном потоке
// ❌ ANR
button.setOnClickListener {
val response = RetrofitClient.api.getDataSync() // блокирует 5+ сек
textView.text = response.data
}
// ✅ Асинхронно
button.setOnClickListener {
viewModelScope.launch {
val response = withContext(Dispatchers.IO) {
RetrofitClient.api.getData()
}
textView.text = response.data
}
}
3. Сложные вычисления
// ❌ ANR — медленный алгоритм на главном потоке
button.setOnClickListener {
val result = fibonacci(50) // несколько секунд
textView.text = result.toString()
}
// ✅ Асинхронно
button.setOnClickListener {
viewModelScope.launch {
val result = withContext(Dispatchers.Default) {
fibonacci(50)
}
textView.text = result.toString()
}
}
4. Большие операции с файлами
// ❌ ANR
button.setOnClickListener {
val content = File("/sdcard/large_file.txt").readText() // блокирует
textView.text = content
}
// ✅ Асинхронно
button.setOnClickListener {
viewModelScope.launch {
val content = withContext(Dispatchers.IO) {
File("/sdcard/large_file.txt").readText()
}
textView.text = content
}
}
Правила для избежания ANR
Правило 1: Главный поток (Main/UI thread) должен быть свободным
// Main thread — только для:
// ✅ Обновление UI
// ✅ Обработка событий (click, touch)
// ✅ Работа с View
// Всё остальное → на другие потоки/корутины
Правило 2: Используй корутины для асинхронности
class DataViewModel : ViewModel() {
val data: StateFlow<Data> = flow {
val result = repository.fetchData() // IO thread
emit(result)
}
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), Data())
}
Правило 3: Специальные диспетчеры для разных типов работ
// Dispatchers.Main — обновление UI (по умолчанию)
// Dispatchers.IO — сеть, БД, файлы (до 64 потоков)
// Dispatchers.Default — вычисления (по числу ядер)
// Dispatchers.Unconfined — редко используется
viewModelScope.launch(Dispatchers.Default) {
val result = heavyComputation() // медленное вычисление
withContext(Dispatchers.Main) {
textView.text = result // обновляем UI
}
}
Как отловить ANR в разработке?
1. Включи ANR диалоги в Developer Options:
Settings → Developer options → Show ANR dialogs (или Wait)
2. Используй Strict Mode (только для разработки):
if (BuildConfig.DEBUG) {
StrictMode.setThreadPolicy(
StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectNetwork()
.penaltyLog()
.build()
)
}
3. Проверь logcat для ANR ошибок:
adb logcat | grep ANR
4. Используй Android Profiler в Android Studio:
- Memory, CPU, Network, Energy профили
- Ищи спайки на главном потоке
Практический пример правильной архитектуры
// Repository — асинхронные операции
class UserRepository {
fun getUser(id: Int): Flow<User> = flow {
val user = database.getUser(id) // IO thread
emit(user)
}.flowOn(Dispatchers.IO)
}
// ViewModel — управление UI state
class UserViewModel(private val repo: UserRepository) : ViewModel() {
val user: StateFlow<User?> = repo.getUser(userId)
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
}
// Fragment/Activity — только обновление UI
class UserFragment : Fragment() {
private val viewModel: UserViewModel by viewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.user.collect { user ->
if (user != null) {
textView.text = user.name
}
}
}
}
}
}
Вывод
ANR — это когда приложение зависает на более чем 5 секунд и система показывает диалог закрытия. Главная причина — блокирующие операции на главном потоке. Решение:
- Используй корутины для асинхронных операций
- Сети, БД, файлы → на Dispatchers.IO
- Вычисления → на Dispatchers.Default
- UI обновление → на Dispatchers.Main
- Монитори производительность через Android Profiler