¿Por qué hay una subclase MutableLiveData separada de LiveData?

96

Parece que se MutableLiveDatadiferencia LiveDatasolo por hacer públicos los métodos setValue()y postValue(), mientras que en LiveDataellos están protegidos.

¿Cuáles son algunas razones para crear una clase separada para este cambio y no simplemente definir esos métodos como públicos en LiveDatasí mismos?

En términos generales, ¿esta forma de herencia (el aumento de la visibilidad de ciertos métodos es el único cambio) una práctica bien conocida y cuáles son algunos escenarios en los que puede ser útil (asumiendo que tenemos acceso a todo el código)?

Alexander Kulyakhtin
fuente
10
es una decisión de diseño. LiveDataes inmutable, ya que el cliente no puede cambiar el estado interno, por lo tanto, seguro para subprocesos
Blackbelt

Respuestas:

138

En LiveData - Android información sobre el desarrollador , se puede ver que para LiveData, setValue()y postValue()métodos no son públicos.

Mientras que, en MutableLiveData - Documentación para desarrolladores de Android , puede ver que se MutableLiveDataextiende LiveDatainternamente y también los dos métodos mágicos de LiveDataestá disponible públicamente en esto y son setValue()& postValue().

setValue(): establece el valor y envía el valor a todos los observadores activos, se debe llamar desde el hilo principal .

postValue(): publicar una tarea en el hilo principal para anular el valor establecido por setValue(), se debe llamar desde el hilo de fondo .

Entonces, LiveDataes inmutable . MutableLiveDataes LiveDataque es mutable y seguro para subprocesos .

Sneh Pandya
fuente
36
No es realmente que LiveData sea inmutable, solo que no se puede modificar fuera de la clase ViewModel. La clase ViewModel puede modificarlo como quiera (por ejemplo, un ViewModel temporizador). Usaría MutableLiveData si quisiera modificarlo fuera de la clase ViewModel.
Elliptica
2
Tomemos este escenario, una aplicación con el patrón de repositorio (servidor + sala) donde la sala es la fuente única de la verdad. La aplicación obtiene datos solo de Room, mientras que Room obtiene su actualización desde el servidor. ¿Se debe usar mutableLiveData porque los datos de la sala de actualización del servidor o LiveData se pueden usar?
Dr4ke the b4dass
5
LiveData es abstracto, por lo que no puede crear directamente un objeto LiveData sin extenderlo. MutableLiveData extiende LiveData.
Serdar Samancıoğlu
1
Los enlaces a LiveData y MutableLiveData dirigen a la documentación obsoleta. ¿Por qué cuando sugerí una edición con enlaces reales, fue rechazada?
Daniel
1
@Daniel no está seguro de por qué fue rechazado por otros revisores en la cola de revisión. ¡He aprobado el cambio, gracias! :)
Sneh Pandya
10

Este es el MutableLiveData.javaarchivo completo :

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);
    }
}

Entonces sí, la diferencia viene solo al hacer postValuey setValuepublicar.

Un caso de uso que puedo recordar de mi cabeza es el de la encapsulación usando Backing Property en Kotlin. Puede exponer LiveDatasu Fragmento / Actividad (Controlador de IU) aunque pueda tener MutableLiveDatamanipulación en su ViewModelclase.

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

De esta manera, su UI Controller solo podrá observar valores sin poder editarlos. Obviamente, su UI Controller puede editar valores usando métodos públicos de TempViewModellike incrementCount().

Nota : para aclarar la confusión mutable / inmutable:

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
}
Ananth
fuente
¿Qué es _score?
IgorGanapolsky
0

MutableLiveData se está extendiendo desde LiveData. Los métodos protegidos de LiveData solo pueden ser abordados por uno mismo o subclases. Entonces, en este caso, MutableLiveData, que es una subclase de LiveData, puede acceder a estos métodos protegidos.

Lo que le gustaría hacer es observar una instancia y ver si hay algún cambio. Pero al mismo tiempo, no desea que ningún "extraño" cambie la instancia que está observando. En cierto sentido, esto crea un problema, ya que le gustaría tener un objeto que sea y modificable, para actualizar cualquier estado nuevo, y no modificable, para asegurarse de que nadie que no deba pueda actualizar esta instancia. Estas dos características entran en conflicto entre sí, pero se pueden resolver creando una capa adicional.

Entonces, lo que hace es extender su clase, LiveData, con una clase que puede acceder a sus métodos. La subcapa, en este caso MutableLiveData, puede acceder a los métodos protegidos de su padre (/ super).

Ahora comienza a crear instancias y crea su instancia de observador de MutableLiveData. Al mismo tiempo, crea una instancia de LiveData que hace referencia a esta misma instancia. Debido a que MutableLiveData extiende LiveData, cualquier instancia de MutableLiveData es un objeto LiveData y, por lo tanto, una variable LiveData puede hacer referencia a ella.

Ahora el truco está casi hecho. Expone solo la instancia de LiveData, nadie puede usar sus métodos protegidos, ni puede convertirlo en su super (tal vez en tiempo de compilación, pero no se ejecutaría: error RunTime). Y mantiene la instancia de la subclase real privada, por lo que solo pueden cambiarla aquellos que son propietarios de la instancia, utilizando los métodos de la instancia.

//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(.....)

Ahora la superclase notifica cuando se aplican cambios.

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

Blockquote En términos generales, ¿es esta forma de herencia (el aumento de la visibilidad de ciertos métodos es el único cambio) una práctica bien conocida y cuáles son algunos escenarios en los que puede ser útil (asumiendo que tenemos acceso a todo el código)?

Sí, es bastante conocido y esto descrito anteriormente es un escenario común. Eliminar el patrón de observador y hacerlo en una forma set / get sería igualmente beneficioso para él. Dependiendo de dónde lo implementes, al final no habrá reglas de oro.

Khamaseen
fuente