Что такое Channel в Kotlin и чем он отличается от Flow?
Что проверяют
Различаете ли вы горячий и холодный источник данных и умеете ли выбрать инструмент под задачу: состояние экрана и одноразовое событие требуют разного. В Android вопрос почти всегда заканчивается практикой: что отдавать наружу из ViewModel и как это собирать с оглядкой на жизненный цикл.
Как отвечать
Channel это примитив передачи между корутинами, по сути очередь. Вызов send приостанавливает корутину, receive ждёт значение. Канал горячий: значения существуют независимо от подписчиков, и каждый элемент достаётся ровно одному получателю. Есть ёмкость (по умолчанию встреча без буфера, а также буферизованный, схлопывающий и неограниченный варианты) и стратегия при переполнении.
Flow холодный: это описание работы, которое запускается заново на каждый collect. У каждого сборщика свой независимый поток значений, а сборка живёт ровно столько, сколько корутина, которая её запустила. Отсюда операторы (map, filter, combine, flatMapLatest), смена контекста через flowOn, обработка ошибок через catch.
Практика в Android:
- состояние экрана это StateFlow: горячий, всегда имеет текущее значение, новый подписчик сразу его получает;
- одноразовые события (навигация, показ сообщения) держат приватным каналом и отдают наружу потоком;
- собирать в UI нужно внутри repeatOnLifecycle, иначе сборка продолжится, когда экран не виден.
private val _events = Channel<UiEvent>(Channel.BUFFERED)
val events = _events.receiveAsFlow()Где спотыкаются
Говорят, что send блокирует поток. Он приостанавливает корутину, поток при этом свободен, а ожидание на канале без буфера это обратное давление по замыслу, а не дефект.
Отдают канал наружу и собирают его в двух местах. Каждое событие достаётся одному сборщику, поэтому часть экрана их просто не увидит. Наружу отдают поток, а сам канал держат приватным.
Берут StateFlow для одноразовых событий и получают повтор при повороте экрана: новый подписчик немедленно получает последнее значение и показывает сообщение второй раз. Обратная ошибка это SharedFlow без буфера, где событие теряется, пока подписчика нет.