Perché esiste una sottoclasse MutableLiveData separata di LiveData?


96

Sembra che MutableLiveDatadifferisca LiveDatasolo dal fatto che il filesetValue()postValue() pubblici i metodi e , mentre in LiveDataessi sono protetti.

Quali sono alcuni motivi per creare una classe separata per questo cambiamento e non definire semplicemente quei metodi come pubblici in LiveDatasé?

In generale, una tale forma di ereditarietà (l'aumento della visibilità di alcuni metodi è l'unico cambiamento) è una pratica ben nota e quali sono alcuni scenari in cui può essere utile (supponendo di avere accesso a tutto il codice)?


10
è una decisione progettuale. LiveDataè immutabile, poiché il client non può modificare lo stato interno, quindi thread-safe
Blackbelt

Risposte:


137

In LiveData - Android Developer Documentation , si può vedere che per LiveData, setValue()epostValue() metodi non sono pubbliche.

Considerando che, in MutableLiveData - Documentazione per sviluppatori Android , puoi vedere che, si MutableLiveDataestende LiveDatainternamente e anche i due metodi magici di LiveDatasono disponibili pubblicamente in questo e sono setValue()& postValue().

setValue(): imposta il valore e invia il valore a tutti gli osservatori attivi, deve essere chiamato dal thread principale .

postValue(): invia un'attività al thread principale per sovrascrivere il valore impostato da setValue(), deve essere chiamato dal thread in background .

Quindi, LiveDataè immutabile . MutableLiveDataè LiveDatache è mutabile e thread-safe .


36
Non è proprio che LiveData sia immutabile, solo che non può essere modificato al di fuori della classe ViewModel. La classe ViewModel può modificarlo come vuole (ad esempio un timer ViewModel). Dovresti usare MutableLiveData se desideri modificarlo al di fuori della classe ViewModel.
Elliptica

2
Prendiamo questo scenario, un'app con il pattern di repository (Server + Room) dove Room è l'unica fonte di verità. L'app riceve i dati solo da Room, mentre Room riceve l'aggiornamento dal server. MutableLiveData è il must perché è possibile utilizzare i dati della sala di aggiornamento del server o LiveData?
Dr4ke il b4dass

5
LiveData è astratto, quindi non è possibile creare direttamente un oggetto LiveData senza estenderlo. MutableLiveData estende LiveData.
Serdar Samancıoğlu

1
Collegamenti a LiveData e MutableLiveData diretti alla documentazione obsoleta. Perché quando ho suggerito una modifica con link effettivi è stata rifiutata?
Daniel

1
@Daniel non è sicuro del motivo per cui è stato rifiutato da altri revisori nella coda delle revisioni. Ho approvato la modifica, grazie! :)
Sneh Pandya

10

Questo è l'intero MutableLiveData.javafile:

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

Quindi sì, la differenza arriva solo facendo postValueesetValue pubblicamente.

Un caso d'uso che posso ricordare dalla mia testa è l'incapsulamento utilizzando la proprietà di supporto in Kotlin. Puoi esporre LiveDataal tuo frammento / attività (controller dell'interfaccia utente) anche se puoi averlo MutableLiveDataper la manipolazione nella tua ViewModelclasse.

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

In questo modo il tuo controller dell'interfaccia utente potrà solo osservare i valori senza essere in grado di modificarli. Ovviamente, il controller dell'interfaccia utente può modificare i valori utilizzando metodi pubblici TempViewModelsimili incrementCount().

Nota : per chiarire la confusione mutabile / immutabile -

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
}

Che cos'è _score?
IgorGanapolsky

0

MutableLiveData si estende da LiveData. I metodi protetti di LiveData possono essere indirizzati solo da sé o da sottoclassi. Quindi in questo caso MutableLiveData essendo una sottoclasse di LiveData può accedere a questi metodi protetti.

Quello che vorresti fare è osservare su un'istanza e vedere se ci sono cambiamenti. Ma allo stesso tempo non vuoi che nessun "estraneo" cambi quell'istanza che stai osservando. In un certo senso questo crea un problema, poiché vorresti avere un oggetto che è e modificabile, per aggiornare qualsiasi nuovo stato, e non modificabile, per assicurarti che nessuno che non dovrebbe può aggiornare questa istanza. Queste due funzionalità sono in conflitto tra loro ma possono essere risolte creando un livello aggiuntivo.

Quindi quello che fai è estendere la tua classe, LiveData, con una classe che può accedere ai suoi metodi. Il sottolivello, in questo caso MutableLiveData, è in grado di accedere ai metodi protetti del suo genitore (/ super).

Ora inizi a creare istanze e crea la tua istanza di osservatore di MutableLiveData. Allo stesso tempo crei un'istanza LiveData che fa riferimento a questa stessa istanza. Poiché MutableLiveData estende LiveData, qualsiasi istanza di MutableLiveData è un oggetto LiveData e può quindi essere referenziato da una variabile LiveData.

Ora il trucco è quasi finito. Esponi solo l'istanza di LiveData, nessuno può usare i suoi metodi protetti, né può lanciarlo al suo super (forse in fase di compilazione, ma non verrà eseguito: errore di RunTime). E mantieni privata l'istanza della sottoclasse effettiva, quindi può essere modificata solo da coloro che possiedono l'istanza, utilizzando i metodi dell'istanza.

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

Ora la super classe notifica quando vengono applicate eventuali modifiche.

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

Blockquote In generale, una tale forma di ereditarietà (l'aumento della visibilità di alcuni metodi è l'unico cambiamento) una pratica ben nota e quali sono alcuni scenari in cui può essere utile (supponendo di avere accesso a tutto il codice)?

Sì, è abbastanza noto e questo descritto sopra è uno scenario comune. Rimuovere il pattern dell'osservatore e crearlo in una forma set / get ne trarrebbe altrettanto vantaggio. A seconda di dove lo si implementa, nessuna regola d'oro alla fine.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.