Warum gibt es eine separate MutableLiveData-Unterklasse von LiveData?

95

Es sieht so MutableLiveDataaus, als würde es sich LiveDatanur dadurch unterscheiden, dass die Methoden setValue()und veröffentlicht postValue()werden, während LiveDatasie geschützt sind.

Was sind einige Gründe, eine separate Klasse für diese Änderung zu erstellen und diese Methoden nicht einfach als öffentlich an LiveDatasich zu definieren?

Ist eine solche Form der Vererbung (die Erhöhung der Sichtbarkeit bestimmter Methoden ist die einzige Änderung) im Allgemeinen eine bekannte Praxis, und in welchen Szenarien kann sie nützlich sein (vorausgesetzt, wir haben Zugriff auf den gesamten Code)?

Alexander Kulyakhtin
quelle
10
Es ist eine Designentscheidung. LiveDataist unveränderlich, da der Client den internen Status nicht ändern kann, daher threadsicher
Blackbelt

Antworten:

135

In Livedata - Android Developer Dokumentation , können Sie sehen , dass für LiveData, setValue()und postValue()Methoden sind nicht öffentlich.

Während in MutableLiveData - Android Developer - Dokumentation , dass Sie sehen können, MutableLiveDataerstreckt sich nach LiveDatainnen und auch die beiden magischen Methoden LiveDataist öffentlich in dieser zur Verfügung und sie sind setValue()& postValue().

setValue(): setze den Wert und versende den Wert an alle aktiven Beobachter, muss vom Haupt-Thread aufgerufen werden .

postValue(): poste eine Aufgabe an den Hauptthread, um den von festgelegten Wert zu überschreiben setValue(), muss vom Hintergrundthread aufgerufen werden .

Ist LiveDataalso unveränderlich . MutableLiveDataist LiveDatadas, was veränderlich und threadsicher ist .

Sneh Pandya
quelle
34
Es ist nicht wirklich so, dass LiveData unveränderlich ist, nur dass es außerhalb der ViewModel-Klasse nicht geändert werden kann . Die ViewModel-Klasse kann es beliebig ändern (z. B. ein ViewModel-Timer). Sie würden MutableLiveData verwenden, wenn Sie es außerhalb der ViewModel-Klasse ändern möchten.
Elliptica
2
Nehmen wir dieses Szenario, eine App mit dem Repository-Muster (Server + Raum), in der Raum die einzige Quelle der Wahrheit ist. Die App ruft nur Daten von Room ab, während Room die Aktualisierung vom Server erhält. Ist mutableLiveData das Muss, weil die Daten aus dem Server-Update-Raum oder LiveData verwendet werden können?
Dr4ke the b4dass
5
LiveData ist abstrakt, daher können Sie ein LiveData-Objekt nicht direkt erstellen, ohne es zu erweitern. MutableLiveData erweitert LiveData.
Serdar Samancıoğlu
1
Links zu LiveData und MutableLiveData führen direkt zu veralteter Dokumentation. Warum wurde eine Bearbeitung mit tatsächlichen Links abgelehnt, wenn ich sie vorschlug?
Daniel
1
@ Daniel ist sich nicht sicher, warum es von anderen Überprüfern in der Überprüfungswarteschlange abgelehnt wurde. Ich habe die Änderung genehmigt, danke! :)
Sneh Pandya
8

Dies ist die gesamte MutableLiveData.javaDatei:

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

Also ja, der Unterschied kommt nur durch machen postValueund setValueöffentlich.

Ein Anwendungsfall, an den ich mich sofort erinnern kann, ist die Kapselung mit Backing Property in Kotlin. Sie können LiveDatasich Ihrem Fragment / Ihrer Aktivität (UI-Controller) aussetzen , obwohl Sie dies MutableLiveDatain Ihrer ViewModelKlasse manipulieren können .

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

Auf diese Weise kann Ihr UI-Controller nur Werte beobachten, ohne sie bearbeiten zu können. Offensichtlich kann Ihr UI-Controller Werte mit öffentlichen Methoden TempViewModelwie " Gefällt mir" bearbeiten incrementCount().

Hinweis : Um veränderliche / unveränderliche Verwirrung zu klären -

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
quelle
Was ist _score?
IgorGanapolsky
0

MutableLiveData wird von LiveData erweitert. Die geschützten Methoden von LiveData können nur von sich selbst oder von Unterklassen angesprochen werden. In diesem Fall kann MutableLiveData, eine Unterklasse von LiveData, auf diese geschützten Methoden zugreifen.

Was Sie tun möchten, ist eine Instanz zu beobachten und festzustellen, ob sich Änderungen ergeben. Gleichzeitig möchten Sie jedoch nicht, dass "Außenstehende" die von Ihnen beobachtete Instanz ändern. In gewissem Sinne führt dies zu einem Problem, da Sie ein Objekt haben möchten, das geändert werden kann, um jeden neuen Status zu aktualisieren und nicht zu ändern, um sicherzustellen, dass niemand, der diese Instanz nicht aktualisieren sollte. Diese beiden Funktionen stehen in Konflikt miteinander, können jedoch durch Erstellen einer zusätzlichen Ebene gelöst werden.

Sie erweitern also Ihre Klasse LiveData um eine Klasse, die auf ihre Methoden zugreifen kann. Die Unterschicht, in diesem Fall MutableLiveData, kann auf die geschützten Methoden ihres übergeordneten Elements (/ super) zugreifen.

Jetzt erstellen Sie Instanzen und erstellen Ihre Beobachterinstanz von MutableLiveData. Gleichzeitig erstellen Sie eine LiveData-Instanz, die auf dieselbe Instanz verweist. Da MutableLiveData LiveData erweitert, ist jede MutableLiveData-Instanz ein LiveData-Objekt und kann daher von einer LiveData-Variablen referenziert werden.

Jetzt ist der Trick fast fertig. Sie machen nur die LiveData-Instanz verfügbar, niemand kann ihre geschützten Methoden verwenden oder sie in Super umwandeln (möglicherweise zur Kompilierungszeit, aber sie würde nicht ausgeführt: RunTime-Fehler). Und Sie halten die eigentliche Unterklasseninstanz privat, sodass sie nur von denjenigen geändert werden kann, denen die Instanz gehört, und zwar mithilfe der Methoden der Instanz.

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

Jetzt benachrichtigt die Superklasse, wenn Änderungen angewendet werden.

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

Blockquote Ist eine solche Form der Vererbung (die Erhöhung der Sichtbarkeit bestimmter Methoden ist die einzige Änderung) im Allgemeinen eine bekannte Praxis, und in welchen Szenarien kann sie nützlich sein (vorausgesetzt, wir haben Zugriff auf den gesamten Code)?

Ja, es ist ziemlich bekannt und dies ist ein häufiges Szenario. Entfernen Sie das Beobachtermuster und erstellen Sie es in einer Set / Get-Form. Dies würde genauso viel davon profitieren. Je nachdem, wo Sie es implementieren, gibt es am Ende keine goldenen Regeln.

Khamaseen
quelle