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


96

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

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

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


10
это дизайнерское решение. LiveDataявляется неизменным, так как клиент не может изменить внутреннее состояние, поэтому потокобезопасен
Blackbelt

Ответы:


138

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

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

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

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

Таким образом, LiveDataявляется непреложным . MutableLiveDataэто LiveDataчто изменчиво и поточно- .


36
На самом деле LiveData не является неизменным, просто его нельзя изменить за пределами класса ViewModel. Класс ViewModel может изменять его как угодно (например, ViewModel таймера). Вы должны использовать MutableLiveData, если хотите изменить его вне класса ViewModel.
Elliptica

2
Возьмем этот сценарий, приложение с шаблоном репозитория (Сервер + Комната), где Комната является Единственным Источником Истины. Приложение получает данные только из Room, а Room - с сервера. Обязательно ли использовать mutableLiveData, потому что можно использовать данные из комнаты обновления сервера или LiveData?
Dr4ke the b4dass

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

1
Ссылки на LiveData и MutableLiveData ведут прямо на устаревшую документацию. Почему, когда я предложил правку с реальными ссылками, она была отклонена?
Дэниел

1
@Daniel не уверен, почему он был отклонен другими рецензентами в очереди на рассмотрение. Я одобрил изменение, спасибо! :)
Сне Pandya

10

Это весь 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 Property в Kotlin. Вы можете подвергнуть 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
}

Что есть _score?
Игорь Ганапольский

0

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, и это принесет не меньшую пользу. В зависимости от того, где вы это реализуете, в конце концов не будет золотых правил.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.