Якщо ви тільки починаєте знайомитися зі світом Android, ви напевно стикалися з вічною дилемою, як обробляти дані на рівні представлення. Довгий час LiveData панувала безроздільно, але з появою корутин Kotlin з'явилися набагато потужніші та гнучкіші інструменти. Йдеться про StateFlow та SharedFlow , які, по суті, є природною еволюцією реактивного та ефективного управління потоками даних.
Правда в тому, що хоча спочатку ці концепції можуть здаватися схожими, кожна з них має своє власне призначення. Бажати, щоб екран запам'ятовував, де зупинилася стрічка новин, не те саме, що бажати надіслати швидке сповіщення про уривчасте повідомлення, яке слід переглянути лише один раз. Щоб уникнути плутанини, давайте розберемо, як працюють ці гарячі потоки та чому вам варто розглянути можливість залишити LiveData позаду у ваших сучасних проектах.
Поняття потоку: холодний чи гарячий?
Щоб зрозуміти StateFlow та SharedFlow, спочатку потрібно зрозуміти різницю між холодним та гарячим потоками. Стандартний потік називається холодним , тобто він схожий на рецепт: нічого не відбувається, доки хтось не вирішить його виконати (виклик `collect`). Проблема полягає в тому, що якщо у вас є три підписники, код продюсера виконується тричі, що може розрядити батарею або призвести до надлишкових запитів API.
Саме тут і з'являються гарячі потоки. Як StateFlow, так і SharedFlow є стратегічно гарячими , тобто продюсер залишається активним незалежно від того, чи хтось слухає. Це дозволяє кільком збирачам даних спільно використовувати одне й те саме джерело даних , запобігаючи повторному виконанню застосунком однієї й тієї ж роботи, що життєво важливо для підтримки плинності системи.
StateFlow: Ідеальна заміна LiveData
StateFlow — це, по суті, контейнер станів. Його головна особливість полягає в тому, що він завжди підтримує поточне значення , яке можна синхронно запитувати за допомогою властивості .value. Це ідеальний інструмент для керування станом інтерфейсу користувача, наприклад, чи завантажується екран, чи успішно оновилися дані профілю.
На відміну від LiveData, StateFlow вимагає початкового значення в своєму конструкторі, що усуває невизначеність наявності нульових станів під час запуску. Крім того, він реалізує поведінку конфляції та дедуплікації : якщо ви видаєте одне й те саме значення двічі поспіль, потік просто ігнорує його. Це чудово підходить для запобігання непотрібному оновленню інтерфейсу, коли дані фактично не змінилися.
Щоб реалізувати це у ViewModel, найкращим підходом є використання приватної резервної властивості MutableStateFlow та надання її як незмінного StateFlow. Це гарантує, що лише ViewModel може змінювати стан, дотримуючись односпрямованого потоку даних та запобігаючи маніпуляціям даними з боку ViewModel.
SharedFlow: Шина подій для унікальних дій
Якщо StateFlow призначений для "стану", SharedFlow призначений для "подій". Уявіть, що ви хочете відобразити діалогове вікно помилки або перейти на інший екран. Якщо ви використовуєте StateFlow, обертання пристрою призведе до того, що новий колектор отримає останнє значення, і подія спрацює знову — серйозна помилка. SharedFlow вирішує цю проблему, оскільки його можна налаштувати зі значенням відтворення, рівним нулю.
Цей потік набагато гнучкіший і дозволяє нам налаштовувати такі параметри, як буфер переповнення . Якщо буфер заповнюється, ми можемо призупинити відправника або відкинути найстаріше чи найновіше значення. Ключовою перевагою є те, що SharedFlow дозволяє надсилати повторювані значення ; якщо вам потрібно надіслати одне й те саме повідомлення "Помилка підключення" тричі, SharedFlow зробить це без вагань, тоді як StateFlow відфільтрує їх.
Технічне порівняння: StateFlow проти SharedFlow проти LiveData
Щоб уникнути плутанини на технічній співбесіді або під час проектування архітектури, важливо враховувати критичні моменти. LiveData простий і автоматично керує життєвим циклом , але він дуже пов'язаний з Android. StateFlow та SharedFlow, будучи частиною корутин Kotlin, є платформонезалежними , що значно спрощує модульне тестування та створення чистих шарів домену.
- StateFlow: Примусово встановлює початкове значення, має .value, видаляє дублікати значень та ідеально підходить для станів інтерфейсу користувача.
- Спільний потікВін не вимагає початкового значення, налаштовується з точки зору повторного відтворення та є правильним варіантом для ефемерних подій.
- LiveDataНе потребує початкового значення, знає про життєвий цикл, але менш потужний в асинхронних перетвореннях.
Важливість безпечного збору врожаю
Дуже поширеною помилкою є виконання `collect` в межах простої області видимості корутини в Activity. Це небезпечно, оскільки потік продовжуватиме обробляти дані, навіть коли програма працює у фоновому режимі , що може призвести до збоїв або надмірного використання процесора. Сучасним рішенням є використання API `repeatOnLifecycle(Lifecycle.State.STARTED)`.
Ця функція призводить до активації колекції, коли представлення видиме, та автоматичного скасування, коли воно досягає стану STOPPED . Для Jetpack Compose рекомендується використовувати `collectAsStateWithLifecycle()` , який інтегрує цю логіку нативно та забезпечує ефективне використання ресурсів пристрою застосунком.
Перетворення холодних потоків на гарячі
Іноді у нас є потік, що надходить з бази даних (наприклад, Room) або репозиторію, і він холодний. Щоб зробити його гарячим, ми використовуємо оператори `stateIn` або `shareIn` . Ці оператори вимагають `CoroutineScope` та стратегії запуску. Найпоширенішим є `SharingStarted.WhileSubscribed(5000)`.
Ця конфігурація чудова, оскільки вона підтримує активність потоку, поки є підписники, а якщо екран закритий, трансляція чекає 5 секунд, перш ніж зупинити її. Це важливо, щоб під час обертання екрана поток не був знищений та створений заново, що дозволить уникнути непотрібних запитів до сервера або локальної бази даних.
Оволодіння цими інструментами передбачає розуміння того, що стан має бути постійним та дедуплікованим за допомогою StateFlow, тоді як одноразові дії повинні проходити через SharedFlow. Поєднуючи це з усвідомленням життєвого циклу та перетворенням холодного потоку за допомогою stateIn, ми досягаємо надійної, повністю реактивної архітектури Android, оптимізованої для реальної продуктивності на мобільних пристроях. Поділіться цим посібником та допоможіть іншим дізнатися про цю тему.
