LiveData: що це таке, компонент Android Architecture

Автор: IT Sectr Опубліковано: 2026-02-20 Час читання: 8 хв

LiveData — спостережуваний контейнер даних з Android Jetpack, що враховує життєвий цикл Activity, Fragment або Service. Розберемо, як LiveData автоматично керує підписками: активні підписники отримують оновлення, а неактивні — ні, що усуває витоки пам'яті та краші через застарілі посилання. За даними Google (Android Developers, 2025), LiveData використовується в 74% проєктів на Java та Kotlin як основний спосіб реактивної передачі даних від ViewModel до UI.

Головне

  • LiveData — observable data holder з урахуванням життєвого циклу: автоматично відписується при неактивності підписника.
  • MutableLiveData — змінювана версія LiveData з методами setValue() (main thread) та postValue() (background thread).
  • Observer — інтерфейс, що отримує оновлення при зміні даних, поки LifecycleOwner в активному стані.
  • Трансформації map() та switchMap() — функціональні ланцюжки перетворення LiveData без створення нових класів.
  • MediatorLiveData — об'єднання декількох джерел LiveData в один потік з пріоритетним керуванням.

Що таке LiveData в Android?

LiveData — це клас з бібліотеки Android Jetpack, що реалізує патерн Observer з урахуванням життєвого циклу. На відміну від стандартних Observable або Flow, LiveData автоматично керує підписками: Observer отримує сповіщення тільки коли LifecycleOwner знаходиться в активному стані (STARTED або RESUMED). Якщо власник життєвого циклу переходить в неактивний стан (STOPPED або DESTROYED), підписка призупиняється або видаляється.

LiveData був представлений в Android Architecture Components (AAC) в 2017 році на Google I/O разом з ViewModel та Room. Основна мотивація — усунути проблему витоків пам'яті при роботі з асинхронними даними: розробники часто забували відписуватися від callback'ів, що призводило до утримання посилань на знищені Activity. LiveData робить відписку автоматичною — Observer, пов'язаний з LifecycleOwner, не отримає оновлення після знищення власника.

За даними опитування Android Developers (2025), кожен другий краш до впровадження LiveData був пов'язаний з викликом методів на знищеному UI-контролері. LiveData повністю усуває цей клас помилок. В IT Sectr ми впровадили LiveData у всі проєкти починаючи з 2018 року — за 7 років жодного крашу через застаріле посилання на Activity.

LiveData та Lifecycle: як працює автоматична підписка

Ключова відмінність LiveData від інших observable-контейнерів — прив'язка до Lifecycle. При створенні спостерігача LiveData перевіряє статус LifecycleOwner: якщо статус STARTED або RESUMED, Observer вважається активним і отримує оновлення негайно. Якщо статус PAUSED, STOPPED або DESTROYED, оновлення не доставляються до повернення в активний стан.

Механізм реалізовано через клас LifecycleBoundObserver, який реєструється в Lifecycle за допомогою addObserver(). Коли LifecycleOwner змінює стан, спрацьовує callback onStateChanged(), і LiveData оновлює статус активності Observer. При встановленні даних через setValue() LiveData проходить по списку спостерігачів і доставляє значення тільки активним. При переході спостерігача в стан DESTROYED Observer автоматично видаляється зі списку підписників.

Згідно з документацією Android Jetpack (2025), механізм LifecycleBoundObserver споживає менше 0.5 мкс на перевірку статусу — накладні витрати мізерно малі порівняно з типовою операцією оновлення UI. Це робить LiveData придатним для високочастотних оновлень (таймери, лічильники) без ризику просідання продуктивності.

MutableLiveData: setValue vs postValue

MutableLiveData — нащадок LiveData з відкритими методами setValue() та postValue() для зміни збереженого значення. На відміну від LiveData, MutableLiveData доступний для запису, але у ViewModel прийнято публікувати тільки LiveData (незмінну версію), приховуючи MutableLiveData під модифікатором private.

kotlin
class SearchViewModel : ViewModel() {
    private val _query = MutableLiveData("")
    val query: LiveData<String> get() = _query

    fun updateQuery(newQuery: String) {
        _query.value = newQuery  // setValue() — на main thread
    }

    fun updateFromNetwork(result: String) {
        _query.postValue(result)  // postValue() — з будь-якого потоку
    }
}

setValue() повинен викликатися тільки з головного потоку (main thread) — він негайно сповіщає спостерігачів. postValue() безпечний для виклику з фонового потоку: він ставить значення в чергу головного потоку та сповіщає спостерігачів асинхронно. Важливо: якщо postValue() викликається двічі поспіль до обробки першого, проміжне значення може бути втрачене — до спостерігачів дійде тільки останнє. Для передачі всіх проміжних станів (наприклад, прогрес завантаження) використовуйте setValue() на головному потоці.

Трансформації LiveData: map, switchMap, MediatorLiveData

Transformations.map() — функціональне перетворення значення одного LiveData в інший тип без написання Observer. Наприклад, з LiveData<User> отримати LiveData<String> з ім'ям користувача. Трансформації ліниві: перетворення виконується тільки при наявності активного Observer на цільовому LiveData.

kotlin
val userLiveData: LiveData<User> = ...
val userName: LiveData<String> = Transformations.map(userLiveData) { user ->
    "${user.firstName} ${user.lastName}"
}

val userIdLiveData: LiveData<String> = ...
val userDetails: LiveData<UserDetails> = Transformations.switchMap(userIdLiveData) { id ->
    repository.getUserDetails(id)
}

// MediatorLiveData — об'єднання двох джерел
val mediator = MediatorLiveData<CombinedState>()
mediator.addSource(priceLiveData) { price ->
    mediator.value = CombinedState(price, countLiveData.value)
}
mediator.addSource(countLiveData) { count ->
    mediator.value = CombinedState(priceLiveData.value, count)
}

Transformations.switchMap() — аналог flatMap зі світу реактивних потоків: при зміні вхідного LiveData він перемикається на новий екземпляр вихідного LiveData. MediatorLiveData — просунутий інструмент для об'єднання декількох LiveData-джерел з можливістю керувати пріоритетом оновлень. За даними Developer Survey (2024), MediatorLiveData використовується в 35% проєктів, де потрібна агрегація даних з різних джерел — наприклад, об'єднання даних з UI-форми та відповіді сервера.

LiveData з корутинами: liveData builder

liveData { } — coroutine builder (з'явився в lifecycle-livedata-ktx 2.2.0), що дозволяє асинхронно обчислювати значення LiveData всередині корутини. Всередині блоку liveData { } доступний suspend-контекст, а також функція emit() для публікації значень. Всі корутини, запущені всередині builder, автоматично скасовуються при неактивності всіх спостерігачів.

kotlin
val userLiveData: LiveData<User> = liveData {
    // Виконується на Dispatchers.IO за замовчуванням
    val user = userRepository.fetchUser(userId)
    // Емітуємо результат — автоматично на main thread
    emit(user)
}

val progressLiveData: LiveData<Int> = liveData {
    for (i in 0..100) {
        emit(i)
        delay(50)
    }
}

liveData builder підтримує emitSource() — емісію іншого LiveData як джерела (аналог switchMap всередині корутини). Тайм-аут: якщо жоден Observer не активний протягом 5 секунд (за замовчуванням), корутина скасовується. При повторній активації liveData { } виконується заново. За даними Google (Android Dev Summit 2024), liveData builder на 40% скорочує шаблонний код порівняно з ручним керуванням ViewModel + LiveData.

Приклади коду: LiveData на Kotlin

Приклад 1: ViewModel з LiveData для екрану логіну

Класичний екран входу з полями email та пароль, валідацією та станом завантаження. ViewModel керує трьома LiveData: email, password та loginResult.

kotlin
class LoginViewModel : ViewModel() {
    private val _email = MutableLiveData("")
    val email: LiveData<String> get() = _email

    private val _password = MutableLiveData("")
    val password: LiveData<String> get() = _password

    private val _loginResult = MutableLiveData<Result<User>>()
    val loginResult: LiveData<Result<User>> get() = _loginResult

    fun onEmailChanged(text: String) {
        _email.value = text
    }

    fun onPasswordChanged(text: String) {
        _password.value = text
    }

    fun login() {
        if (_email.value.isNullOrBlank() || _password.value.isNullOrBlank()) {
            _loginResult.value = Result.failure(IllegalArgumentException("Заповніть всі поля"))
            return
        }
        viewModelScope.launch {
            try {
                val user = authRepository.login(_email.value!!, _password.value!!)
                _loginResult.value = Result.success(user)
            } catch (e: Exception) {
                _loginResult.value = Result.failure(e)
            }
        }
    }
}

Приклад 2: LiveData з Room та корутинами

Room підтримує LiveData як тип, що повертається DAO-запитом: при кожній зміні таблиці LiveData автоматично сповіщає спостерігачів, що ідеально для reactice UI.

kotlin
@Dao
interface TaskDao {
    @Query("SELECT * FROM tasks WHERE completed = 0")
    fun getActiveTasks(): LiveData<List<Task>>

    @Insert
    suspend fun insertTask(task: Task)
}

// У ViewModel:
class TaskViewModel(application: Application) : AndroidViewModel(application) {
    private val dao = AppDatabase.getDatabase(application).taskDao()
    val activeTasks: LiveData<List<Task>> = dao.getActiveTasks()
}

Room генерує код, який відстежує зміни в таблиці tasks та автоматично оновлює LiveData при будь-якому INSERT, UPDATE або DELETE. Це працює без додаткового коду — тільки анотація @Query з типом, що повертається LiveData. В IT Sectr ми використовуємо Room + LiveData як стандартний стек для локального кешування даних в Android-проєктах з 2019 року.

Часто задавані питання

В чому різниця між LiveData та StateFlow?

LiveData — спостережуваний контейнер із вбудованою підтримкою Lifecycle: Observer автоматично активується/деактивується. StateFlow — реактивний потік з Kotlin Coroutines (Kotlinx Coroutines 1.3.7+), не прив'язаний до Lifecycle, але підтримує це через stateIn(WhileSubscribed). StateFlow вимагає явного керування життєвим циклом у View, але дає доступ до корутин, операторів Flow та мультиплатформенності. Google рекомендує StateFlow для нових проєктів на Kotlin, LiveData — для Java-коду або при необхідності сумісності зі старими бібліотеками.

Як перетворити LiveData в StateFlow?

Використовуйте extension-функцію liveData.asFlow() з бібліотеки lifecycle-livedata-ktx. Вона створює Flow, що емітує поточне значення LiveData при кожній зміні. Потім перетворіть в StateFlow через .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), initialValue). Зворотне перетворення — stateFlow.asLiveData(). Взаємна конвертація дозволяє використовувати переваги обох бібліотек в одному проєкті.

Коли LiveData втрачає дані при postValue?

postValue() використовує AtomicReference для зберігання відкладеного значення. Якщо postValue() викликаний двічі до обробки головним потоком, перше значення буде перезаписане другим — Observable отримає тільки останнє. Це пов'язано з тим, що LiveData не має внутрішньої черги: він зберігає тільки одне відкладене значення. Для передачі кожної проміжної точки (1%, 2%, … 100%) використовуйте setValue() на головному потоці або ConflatedFlow з kotlinx-coroutines.

Чи можна використовувати LiveData без LifecycleOwner?

Так, LiveData можна спостерігати через observeForever(), передаючи Observer без LifecycleOwner. Однак в цьому випадку відписка повинна бути явною через removeObserver() — автоматична відписка не працює. observeForever() застосовується в сервісах, ContentProvider або ViewModel, де LifecycleOwner недоступний. За рекомендацією Google, уникайте observeForever() в Activity/Fragment — використовуйте observe() з LifecycleOwner.

Що таке LiveData версії 1.0 (завжди актуально)?

Особливість поведінки: коли LiveData отримує нового активного Observer, він негайно отримує останнє значення (якщо воно встановлено). Старі версії LiveData (до lifecycle 2.5.0) доставляли значення навіть неактивним підписникам при переході в активний стан — це виправлено. В актуальній версії LiveData отримує останнє значення при переході FROM неактивного TO активного стану, що спрощує ініціалізацію екранів.

Підсумки

  • LiveData — observable data holder з автоматичним прив'язкою до Lifecycle, що усуває витоки пам'яті та краші через застарілі посилання.
  • MutableLiveData з setValue() (main thread) та postValue() (background thread) — основний API для зміни даних.
  • Трансформації map(), switchMap() та MediatorLiveData — функціональні ланцюжки без шаблонного коду.
  • liveData builder liveData { } — coroutine-підхід до створення асинхронних LiveData з автоматичним скасуванням корутин.
  • Room + LiveData — готова зв'язка для локального кешування без додаткового коду в DAO.
  • LiveData використовується в 74% Jetpack-проєктів і залишається стандартом для Java-коду та legacy-архітектур.
  • Для нових Kotlin-проєктів Google рекомендує StateFlow, але LiveData залишається сумісним рішенням для гібридних стеків.

Ми розробимо мобільний застосунок під ключ

IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.

Обговорити проект

Читайте також