Сопоставление LiveData, SingleLiveEvent и MediatorLiveData в Android

Введение

В этой статье подробно рассмотрим LiveData, а также его подклассы: SingleLiveEvent и MediatorLiveData. LiveData  —  это один из блоков Android Architecture Components, предоставляющий наблюдаемый держатель данных. Он позволяет создавать реактивные и учитывающие жизненный цикл компоненты в приложениях Android. Назначение LiveData  —  управление данными в приложениях Android  —  особенно важно при обновлении пользовательского интерфейса.

LiveData

LiveData  —  это класс, который хранит объект данных и позволяет наблюдателям получать обновления при каждом изменении данных. Это реализация паттерна “Observer” (“Наблюдатель”), специально разработанная для использования в приложениях Android. LiveData учитывает жизненный цикл, то есть обновляет наблюдателей, только когда они находятся в активном состоянии.

Ключевые особенности LiveData:

  • Учет состояния жизненного цикла. LiveData ориентирован на жизненный цикл, что означает автоматическое управление подписчиками на основе состояния жизненного цикла компонентов. Это предотвращает потенциальные утечки памяти и гарантирует выполнение обновлений пользовательского интерфейса, только когда компонент активен.
  • Автоматические обновления. LiveData уведомляет наблюдателей, когда хранящиеся в нем данные изменяются. Это устраняет необходимость в ручной обработке обновлений пользовательского интерфейса и сокращает объем кода.
  • Согласованность данных. LiveData гарантирует получение наблюдателями самых свежих данных. Когда состояние жизненного цикла изменяется (например, при переходе с фонового режима на передний план), LiveData запускает обновление, предоставляя наблюдателям актуальные данные.

Для создания объекта LiveData можно использовать класс MutableLiveData, который является подклассом LiveData. Это позволяет устанавливать начальное значение объекта данных и впоследствии его обновлять. Вот пример того, как создать и наблюдать объект MutableLiveData:

val liveData = MutableLiveData<String>()
liveData.setValue("Hello, world!")
liveData.observe(this, object : Observer<String?>() {
fun onChanged(s: String) {
Log.d(TAG, "onChanged: $s")
}
})

В этом примере создаем объект MutableLiveData и устанавливаем его значение в “Hello, world!”. Затем наблюдаем за объектом LiveData, передавая ему экземпляр Activity или Fragment и коллбэк-функцию, которая будет вызываться при любом изменении данных. В этом случае записываем новое значение объекта данных в консоль.

SingleLiveEvent

SingleLiveEvent  —  это подкласс LiveData, предназначенный для обработки событий в приложениях Android. Он решает проблему повторного срабатывания событий при изменении конфигурации (например, при повороте экрана) в процессе использования LiveData.

Ключевые особенности SingleLiveEvent:

  • Потребление событий. SingleLiveEvent гарантирует единоразовое потребление событий. Когда наблюдатель активен, он получает событие. Однако если после этого будет зарегистрирован новый наблюдатель, он не получит ранее выпущенное событие. Таким образом, предотвращается повторное срабатывание событий при изменении конфигурации.
  • Учет состояния жизненного цикла. SingleLiveEvent наследует поведение LiveData, учитывающее жизненный цикл. Он запускает событие, только если есть хотя бы один активный наблюдатель, и избегает запуска событий, когда наблюдатель неактивен или удален.
  • Post Value. SingleLiveEvent предоставляет метод postValue() для установки значения события, гарантируя доставку события только активным наблюдателям.

Чтобы создать объект SingleLiveEvent, можно использовать следующий код:

class SingleLiveEvent<T> : MutableLiveData<T>() {
private val mPending = AtomicBoolean(false)
@MainThread
override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
if (hasActiveObservers()) {
Log.w(TAG, "Multiple observers registered but only one will be notified of changes.")
}
// Наблюдение за внутренним MutableLiveData
super.observe(owner) { t ->
if (mPending.compareAndSet(true, false)) {
observer.onChanged(t)
}
}
}
@MainThread
override fun setValue(t: T?) {
mPending.set(true)
super.setValue(t)
}
}

В этом примере создаем класс SingleLiveEvent, который расширяет MutableLiveData. Класс SingleLiveEvent переопределяет методы setValue() и observe() для обеспечения механизма отправки и наблюдения событий.

Метод setValue() устанавливает флаг, указывающий на то, что событие ожидается, а затем вызывает реализацию суперкласса setValue(). Метод observe() смотрит, есть ли активные наблюдатели, выдавая предупреждение при наличии нескольких наблюдателей. Затем он устанавливает нового наблюдателя, который проверяет флаг ожидания и посылает событие только в случае значения true. 

SingleLiveEvent позволяет обеспечить отправку событий только один раз и избежать проблем с дублированием событий, обрабатываемых наблюдателями.

MediatorLiveData

MediatorLiveData  —  это еще один подкласс LiveData, который позволяет объединить несколько источников LiveData в один. Это может быть полезно, когда нужно наблюдать за несколькими источниками данных и реагировать на обновления любого из них.

Объединение нескольких источников LiveData в один

Ключевые особенности MediatorLiveData:

  • Объединение данных. MediatorLiveData может наблюдать за несколькими источниками LiveData и объединять их данные в один источник. Это полезно, когда необходимо объединить или преобразовать данные из разных источников перед обновлением пользовательского интерфейса.
  • Пользовательская логика. MediatorLiveData позволяет определять пользовательскую логику для установления порядка и времени обновления объединенных данных. Это позволяет настроить триггеры, условия и преобразования на основе наблюдаемых источников LiveData.
  • Динамическая композиция. MediatorLiveData предоставляет возможность динамически добавлять или удалять источники во время выполнения программы. Такая гибкость позволяет адаптировать состав данных в соответствии с изменяющимися требованиями приложения.

Чтобы использовать MediatorLiveData, можно зарегистрировать другие объекты LiveData в качестве источников и определить коллбэк-функцию, которая будет вызываться, когда любой из объектов-источников LiveData изменится. Вот пример:

val mediatorLiveData = MediatorLiveData<String>()
val source1: LiveData<String> = MutableLiveData()
val source2: LiveData<String> = MutableLiveData()

mediatorLiveData.addSource(source1) { s ->
mediatorLiveData.value = s
}

mediatorLiveData.addSource(source2) { s ->
mediatorLiveData.value = s
}

mediatorLiveData.observe(this, Observer { s ->
Log.d(TAG, "onChanged: $s")
})

В этом примере создаем объект MediatorLiveData и регистрируем два других объекта LiveData  —  source1 и source2  —  в качестве источников. Для каждого источника определяем функцию обратного вызова, которая устанавливает значение объекта MediatorLiveData при каждом изменении источника.

Затем наблюдаем за объектом MediatorLiveData и записываем новое значение в консоль при любом его изменении. В этом случае объект MediatorLiveData будет обновляться при каждом изменении одного из источников.

MediatorLiveData позволяет объединять и реагировать на изменения из нескольких источников данных, обеспечивая гибкость в управлении сложными потоками данных в Android-приложении.

Заключение

LiveData, SingleLiveEvent и MediatorLiveData  —  мощные классы, предлагаемые Android Architecture Components. Эти классы помогают управлять данными и событиями в Android-приложении. LiveData предоставляет наблюдаемый держатель данных для обновления пользовательского интерфейса, SingleLiveEvent предназначен для обработки событий, а MediatorLiveData позволяет объединять и реагировать на изменения из нескольких источников данных. С помощью этих классов вы сможете создавать более удобный в обслуживании код в Android-проектах.

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

Читайте нас в TelegramVK и Дзен


Перевод статьи halil ibrahim saltoğlu: Comparison of LiveData, SingleLiveEvent, and MediatorLiveData in Android

Предыдущая статьяКак правильно обрабатывать события 
Следующая статьяNetMock: простой подход к тестированию HTTP-запросов в Java, Android и Kotlin Multiplatform