Room + Flow: когда база данных сама обновляет интерфейс

Классический подход к работе с базой данных в Android выглядит так: отправил запрос, получил результат, обновил UI. Понадобились свежие данные — отправил ещё один запрос. Добавил запись — снова запросил весь список. Удалил — запросил. Изменил — запросил. Схема «запрос-ответ» работает, но заставляет разработчика вручную синхронизировать данные с интерфейсом. Room предлагает другой подход. Вместо разовых … Читать далее

NetworkResult: оборачиваем сетевой запрос в sealed-класс

Любой сетевой запрос в приложении может закончиться тремя способами: данные загружаются, данные получены, что-то пошло не так. Эти три состояния — Loading, Success, Error — повторяются из экрана в экран, из проекта в проект. Каждый раз писать try/catch, вручную переключать флаги isLoading и isError — утомительно и чревато ошибками. Вместо этого можно упаковать все три … Читать далее

flatMapLatest и семейство *Latest: отменяй устаревшее, показывай актуальное

В предыдущей статье мы научились фильтровать входной поток: убрали дребезг, дубликаты, пустые запросы. Но мы не затронули другую проблему: что происходит, если обработка одного значения занимает больше времени, чем интервал между двумя значениями? Представьте: пользователь ищет рецепты. Набрал «пирог» — приложение отправило запрос. Пока сервер думает, пользователь передумал и исправил на «пирожки». Приложение отправило второй … Читать далее

debounce, distinctUntilChanged и filter: три оператора, без которых не обходится ни один поиск

Представьте: пользователь открывает строку поиска и начинает набирать запрос. Каждый символ порождает новое значение в потоке: Если реагировать на каждый символ, приложение отправит шесть запросов к серверу за полторы секунды. Пять из них бессмысленны: пользователь ещё не закончил печатать. Сервер нагружен, батарея садится, а интерфейс дёргается, показывая промежуточные результаты, которые тут же устаревают. Kotlin Flow … Читать далее

stateIn() и shareIn(): как подогреть холодный Flow для UI

Мы уже выяснили, что холодный Flow плохо дружит с Compose: при каждой рекомпозиции поток перезапускается с нуля. Решение — превратить его в горячий. Но как именно? В Kotlin есть два оператора-подогревателя: stateIn() и shareIn(). Первый превращает холодный Flow в StateFlow, второй — в SharedFlow. Оба живут в скоупе, который вы им укажете, и делят данные … Читать далее

StateFlow как состояние экрана: паттерн, который стоит выучить наизусть

В Jetpack Compose всё крутится вокруг состояния. Текст в поле ввода — состояние. Индикатор загрузки — состояние. Список товаров в корзине — тоже состояние. Каждый раз, когда состояние меняется, Compose автоматически перерисовывает те части интерфейса, которые от него зависят. Внутри Composable-функций состояние хранится через remember { mutableStateOf(…) }. Но если данные приходят из ViewModel — … Читать далее

Холодный Flow в Android: ловушка для Compose-разработчика

Kotlin Flow — мощный инструмент для работы с асинхронными потоками данных. Но у него есть особенность, которая регулярно ставит в тупик Android-разработчиков: холодный поток ленив. Он не генерирует данные, пока на него никто не подписан. Звучит разумно — зачем тратить ресурсы впустую? Но в связке с Jetpack Compose эта бережливость превращается в ловушку. Эксперимент: репозиторий … Читать далее

LaunchedEffect и rememberCoroutineScope: корутины в Jetpack Compose

В мире View корутины запускались из чётко определённых точек: onCreate(), onResume(), onDestroy(). В Compose этих методов нет. Composable-функции вызываются многократно, перестраиваются при каждом изменении состояния и не гарантируют стабильного жизненного цикла. Запустить корутину прямо в теле Composable-функции — всё равно что поставить чайник на плиту, которая включается и выключается сама по себе. Compose предлагает два … Читать далее

lifecycleScope vs viewModelScope: где запускать корутины в Android

Если вы только что закончили изучать корутины по документации или учебному курсу, то наверняка привыкли к простой схеме: функция main(), билдер runBlocking, пара вызовов delay() — и вот уже корутина делает что-то полезное. В Android этот уютный мир рушится в первую же минуту. Функции main() здесь нет. Блокировать потоки нельзя — главный поток отвечает за … Читать далее

Управление потоком в Kotlin Flow

Поколение большого пальца – вот как нас называли. Какие глубокомысленные переписки мы ухитрялись вести на кнопочных телефонах, набирая текст SMS большим пальцем асинхронно с конспектированием лекций… Когда пользователь печатает или вообще взаимодействует с приложением слишком быстро, возникает опасность гиперотзывчивости. Приложение вынуждено реагировать на всякий чих, а в таких случаях, как известно, не наздравствуешься. Естественно, в Kotlin Flow, … Читать далее