Будет ли UI лагать при запуске длительной операции на IO Dispatcher в ViewModel
Что проверяют
Понимаете ли вы, где на самом деле выполняется код корутины и что именно блокирует главный поток. Короткий ответ «нет, если вся тяжёлая работа действительно ушла на Dispatchers.IO», но интервьюеру важны оговорки. Следом обычно спрашивают про тяжёлые вычисления на IO и про огромный результат, который возвращается в UI.
Как отвечать
viewModelScope по умолчанию работает на Dispatchers.Main.immediate. Код внутри withContext(Dispatchers.IO) уходит в пул фоновых потоков, главный поток в это время свободен и рисует кадры, поэтому сама долгая операция UI не тормозит.
Лаги всё же бывают, и хорошо назвать почему:
- работа до и после
withContext: парсинг большого JSON, сортировка и маппинг списка выполняются на Main, если их не унесли в фон; - вычисления на IO: UI не заморозят, но IO рассчитан на блокирующее ожидание, до 64 потоков или по числу ядер, если их больше. Для CPU-задач правильнее
Dispatchers.Default, у него потоков по числу ядер; - лавина обновлений: если
Flowшлёт в UI сотни значений в секунду, каждое вызывает перерисовку. Помогаютconflate(),debounce(),distinctUntilChanged(); - блокировки: главный поток стоит на
runBlockingили ждёт мониторsynchronized, который держит фоновый код.
Хорошая практика: диспетчер передавать через конструктор, а репозиторий делать main-safe, то есть он сам переключается через withContext, и ViewModel вызывает его с Main без раздумий. В тестах Main подменяют через Dispatchers.setMain и StandardTestDispatcher.
Проверка: StrictMode ловит диск и сеть на главном потоке, Android Studio Profiler и Perfetto показывают, что выполнялось на Main во время пропущенного кадра.
Где спотыкаются
Вызывают Thread.sleep или блокирующий SDK прямо в viewModelScope.launch без смены диспетчера и уверены, что «корутина же асинхронная».
Отвечают просто «нет» и не замечают, что разбор ответа сервера и подготовка списка остались на главном потоке.
Запускают работу в GlobalScope или в своём scope без отмены. Операция переживает экран и держит ссылки, а viewModelScope отменил бы её сам в onCleared().