Por que há uma subclasse separada MutableLiveData de LiveData?

97

Parece que MutableLiveDatadifere LiveDataapenas de tornar os métodos setValue()e postValue()públicos, enquanto em LiveDataeles são protegidos.

Quais são alguns motivos para criar uma classe separada para essa mudança e não simplesmente definir esses métodos como públicos em LiveDatasi?

De modo geral, essa forma de herança (aumentar a visibilidade de certos métodos sendo a única mudança) é uma prática bem conhecida e quais são alguns cenários em que pode ser útil (assumindo que temos acesso a todo o código)?

Alexander Kulyakhtin
fonte
11
é uma decisão de design. LiveDataé imutável, pois o cliente não pode alterar o estado interno, portanto thread-safe
Blackbelt

Respostas:

139

Em LiveData - Android Developer Documentation , você pode ver que os métodos LiveData, setValue()& postValue()não são públicos.

Considerando que, em MutableLiveData - Android Developer Documentation , você pode ver que, MutableLiveDatase estende LiveDatainternamente e também os dois métodos mágicos de LiveDataestá publicamente disponível neste e eles são setValue()& postValue().

setValue(): define o valor e despacha o valor para todos os observadores ativos, deve ser chamado do thread principal .

postValue(): postar uma tarefa no thread principal para substituir o valor definido por setValue(), deve ser chamado a partir do thread em segundo plano .

Portanto, LiveDataé imutável . MutableLiveDataé LiveDataque é mutável e seguro para thread .

Sneh Pandya
fonte
39
Não é verdade que LiveData seja imutável, apenas que não pode ser modificado fora da classe ViewModel. A classe ViewModel pode modificá-lo como quiser (por exemplo, um temporizador ViewModel). Você usaria MutableLiveData se quisesse modificá-lo fora da classe ViewModel.
Elliptica
2
Vamos considerar este cenário, um aplicativo com o padrão de repositório (Servidor + Sala) onde Sala é a única fonte da verdade. O aplicativo obtém dados apenas da Room, enquanto a Room obtém a atualização do servidor. O mutableLiveData deve ser usado porque os dados do servidor atualizam o Room ou o LiveData pode ser usado?
Dr4ke the b4dass
5
LiveData é abstrato, portanto, você não pode criar diretamente um objeto LiveData sem estendê-lo. MutableLiveData estende LiveData.
Serdar Samancıoğlu
1
Links para LiveData e MutableLiveData direcionam para documentação obsoleta. Por que quando sugeri uma edição com links reais, ela foi rejeitada?
Daniel
1
@Daniel não sabe ao certo por que foi rejeitado por outros revisores na fila de revisão. Eu aprovei a mudança, obrigado! :)
Sneh Pandya
10

Este é o MutableLiveData.javaarquivo 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);
    }
}

Então, sim, a diferença vem apenas tornando postValuee setValuepublicando.

Um caso de uso que consigo lembrar é para o encapsulamento usando a propriedade Backing em Kotlin. Você pode expor LiveDataao seu Fragmento / Atividade (Controlador de IU), embora você possa ter MutableLiveDatapara manipulação em sua ViewModelclasse.

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

Desta forma, o seu UI Controller só será capaz de observar os valores sem poder editá-los. Obviamente, seu UI Controller pode editar valores usando métodos públicos TempViewModelsemelhantes incrementCount().

Nota : Para esclarecer a confusão mutável / imutável -

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
fonte
O que é _score?
IgorGanapolsky
0

MutableLiveData é uma extensão do LiveData. Os métodos protegidos do LiveData só podem ser endereçados por self ou subclasses. Portanto, neste caso, MutableLiveData, sendo uma subclasse de LiveData, pode acessar esses métodos protegidos.

O que você gostaria de fazer é observar uma instância e ver se há alguma mudança. Mas, ao mesmo tempo, você não quer que nenhum "estranho" altere a instância que você está observando. Em certo sentido, isso cria um problema, pois você gostaria de ter um objeto que é e pode ser alterado, para atualizar qualquer novo status, e não pode ser alterado, para garantir que ninguém que não deveria pode atualizar esta instância. Esses dois recursos são conflitantes, mas podem ser resolvidos criando uma camada extra.

Portanto, o que você faz é estender sua classe, LiveData, com uma classe que pode acessar seus métodos. A subcamada, neste caso MutableLiveData, é capaz de acessar os métodos protegidos de seu pai (/ super).

Agora você começa a criar instâncias e cria sua instância de observador de MutableLiveData. Ao mesmo tempo, você cria uma instância LiveData referindo-se a essa mesma instância. Como MutableLiveData estende LiveData, qualquer instância de MutableLiveData é um objeto LiveData e, portanto, pode ser referenciada por uma variável LiveData.

Agora o truque está quase pronto. Você expõe apenas a instância LiveData, ninguém pode usar seus métodos protegidos, nem pode lançá-lo em seu super (talvez em tempo de compilação, mas não seria executado: erro de tempo de execução). E você mantém a instância da subclasse real privada, de modo que ela só pode ser alterada por aqueles que possuem a instância, usando os métodos da instância.

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

Agora a superclasse notifica quando qualquer alteração é aplicada.

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

Blockquote De modo geral, essa forma de herança (aumentar a visibilidade de certos métodos sendo a única mudança) é uma prática bem conhecida e quais são alguns cenários em que pode ser útil (assumindo que temos acesso a todo o código)?

Sim, é bastante conhecido e o descrito acima é um cenário comum. Remova o padrão do observador e apenas torná-lo em uma forma set / get teria o mesmo benefício com isso. Dependendo de onde você o implementa, não há regras de ouro no final.

Khamaseen
fonte