Почему существует отдельный подкласс MutableLiveData от LiveData?

Похоже, MutableLiveData отличается от LiveData только тем, что делает методы setValue() и postValue() общедоступными, тогда как в LiveData они защищены.

По каким причинам нужно сделать отдельный класс для этого изменения, а не просто определять эти методы как общедоступные в самом LiveData?

Вообще говоря, является ли такая форма наследования (повышение видимости определенных методов единственным изменением) хорошо известной практикой и в каких сценариях она может быть полезна (при условии, что у нас есть доступ ко всему коду)?


person Alexander Kulyakhtin    schedule 18.10.2017    source источник
comment
это дизайнерское решение. LiveData неизменен, так как клиент не может изменить внутреннее состояние, поэтому потокобезопасный   -  person Blackbelt    schedule 18.10.2017


Ответы (3)


В LiveData - документации для разработчиков Android вы можете увидеть это для LiveData, setValue() & postValue() методы не являются общедоступными.

Принимая во внимание, что в MutableLiveData - документации для разработчиков Android вы можете увидеть это MutableLiveData расширяет LiveData внутри, а также два магических метода LiveData общедоступны в этом, и это setValue() & postValue().

setValue(): установите значение и отправьте его всем активным наблюдателям, должны вызываться из основного потока.

postValue(): отправить задачу в основной поток, чтобы переопределить значение, установленное setValue(), необходимо вызывать из фонового потока.

Итак, LiveData неизменяемый. MutableLiveData LiveData, который изменяемый и потокобезопасный.

person Sneh Pandya    schedule 18.10.2017
comment
На самом деле LiveData не является неизменным, просто его нельзя изменить вне класса ViewModel. Класс ViewModel может изменять его как угодно (например, ViewModel таймера). Вы должны использовать MutableLiveData, если хотите изменить его вне класса ViewModel. - person Elliptica; 20.09.2018
comment
в каких сценариях мне нужно использовать собственный класс Liveata? - person Pavel Poley; 03.10.2018
comment
Возьмем этот сценарий, приложение с шаблоном репозитория (Сервер + Комната), где Комната является Единственным Источником Истины. Приложение получает данные только из Room, а Room - с сервера. Обязательно ли использовать mutableLiveData, потому что можно использовать данные из комнаты обновления сервера или LiveData? - person Dr4ke the b4dass; 13.11.2018
comment
LiveData является абстрактной, поэтому вы не можете напрямую создать объект LiveData, не расширяя его. MutableLiveData расширяет LiveData. - person Serdar Samancıoğlu; 27.12.2018
comment
postValue можно вызывать из любого потока. setValue должен вызываться основным потоком. - person Kurt Huwig; 03.12.2019
comment
Ссылки на LiveData и MutableLiveData ведут прямо на устаревшую документацию. Почему, когда я предложил правку с реальными ссылками, она была отклонена? - person Daniel; 03.12.2019
comment
@Daniel не уверен, почему он был отклонен другими рецензентами в очереди на рассмотрение. Я одобрил изменение, спасибо! :) - person Sneh Pandya; 10.12.2019
comment
Когда мы хотим использовать изменяемые LiveData вместо обычных LiveData? - person IgorGanapolsky; 10.02.2020
comment
@IgorGanapolsk Repo (и, возможно, ViewModel) должны использовать изменяемые LiveData для представления данных, которые изменяются. Представления всегда должны использовать только LiveData. - person Mooing Duck; 13.04.2020

Это весь MutableLiveData.java файл:

package androidx.lifecycle;
/**
 * {@link LiveData} which publicly exposes {@link #setValue(T)} and {@link #postValue(T)} method.
 *
 * @param <T> The type of data hold by this instance
*/
@SuppressWarnings("WeakerAccess")
public class MutableLiveData<T> extends LiveData<T> {
    @Override
    public void postValue(T value) {
        super.postValue(value);
    }
    @Override
    public void setValue(T value) {
        super.setValue(value);
    }
}

Так что да, разница заключается только в том, что postValue и setValue становятся общедоступными.

Один случай использования, который я могу припомнить, - это инкапсуляция с использованием Backing Недвижимость в Котлине. Вы можете предоставить LiveData своему фрагменту / активности (UI-контроллеру), даже если у вас есть MutableLiveData для манипуляций в вашем ViewModel классе.

    class TempViewModel : ViewModel() {
        ...
        private val _count = MutableLiveData<Int>()
        val count: LiveData<Int>
            get() = _count
        public fun incrementCount() = _count.value?.plus(1)
        ...
    }

Таким образом, ваш UI-контроллер сможет только наблюдать значения, не имея возможности их редактировать. Очевидно, что ваш UI-контроллер может редактировать значения, используя общедоступные методы TempViewModel, например incrementCount().

Примечание. Чтобы прояснить путаницу между изменяемыми и неизменяемыми параметрами:

data class User(var name: String, var age: Int)

class DemoLiveData: LiveData<User>()

var demoLiveData: LiveData<User>? = DemoLiveData()

fun main() {
    demoLiveData?.value = User("Name", 23) // ERROR
    demoLiveData?.value?.name = "Name" // NO ERROR
    demoLiveData?.value?.age = 23  // NO ERROR
}
person Ananth    schedule 23.07.2019
comment
Что такое _score? - person IgorGanapolsky; 10.02.2020
comment
Красиво ответил. Спасибо - person arungiri_10; 30.03.2021

MutableLiveData расширяется от LiveData. К защищенным методам LiveData можно обращаться только через себя или подклассы. Таким образом, в этом случае MutableLiveData, являющийся подклассом LiveData, может получить доступ к этим защищенным методам.

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

Итак, что вы делаете, так это расширяете свой класс LiveData с помощью класса, который может получить доступ к его методам. Подуровень, в данном случае MutableLiveData, может получить доступ к защищенным методам своего родителя (/ super).

Теперь вы начинаете создавать экземпляры и создаете свой экземпляр наблюдателя MutableLiveData. В то же время вы создаете экземпляр LiveData, ссылающийся на этот же экземпляр. Поскольку MutableLiveData расширяет LiveData, любой экземпляр MutableLiveData является объектом LiveData и, следовательно, на него может ссылаться переменная LiveData.

Теперь фокус почти готов. Вы открываете только экземпляр LiveData, никто не может использовать его защищенные методы и не может преобразовать его в супер (возможно, во время компиляции, но он не будет работать: ошибка RunTime). И вы сохраняете фактический экземпляр подкласса закрытым, поэтому его могут изменить только те, кто владеет экземпляром, с помощью методов экземпляра.

//create instance of the sub class and keep this private
private val _name: MutableLiveData<String> = MutableLiveData<String>()
//create an instance of the super class referring to the same instance
val name: LiveData<String> = _name
//assign observer to the super class, being unable to change it
name.value.observe(.....)

Теперь суперкласс уведомляет о любых изменениях.

//change the instance by using the sub class
_name.postValue(...)
//or _name.setValue(...)

Blockquote В общем, является ли такая форма наследования (увеличение видимости определенных методов единственным изменением) хорошо известной практикой и в каких сценариях она может быть полезна (при условии, что у нас есть доступ ко всему коду)?

Да, это хорошо известно, и описанный выше сценарий является обычным. Удалите шаблон наблюдателя и просто сделайте его в форме set / get, и это принесет не меньшую пользу. В зависимости от того, где вы это реализуете, никаких золотых правил в итоге не будет.

person Khamaseen    schedule 20.08.2020