← Назад к вопросам

Что такое ANR?

2.0 Middle🔥 241 комментариев
#Многопоточность и асинхронность#Производительность и оптимизация

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

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 секунд и система показывает диалог закрытия. Главная причина — блокирующие операции на главном потоке. Решение:

  1. Используй корутины для асинхронных операций
  2. Сети, БД, файлы → на Dispatchers.IO
  3. Вычисления → на Dispatchers.Default
  4. UI обновление → на Dispatchers.Main
  5. Монитори производительность через Android Profiler