Waarom is er een aparte MutableLiveData-subklasse van LiveData?

Het lijkt erop dat MutableLiveDataalleen verschilt van LiveDatadoor de methoden setValue()en postValue()te gebruiken openbaar, terwijl ze in LiveDatabeschermd zijn.

Wat zijn enkele redenen om een ​​aparte klasse te maken voor deze wijziging en deze methoden niet simpelweg als openbaar te definiëren in de LiveDatazelf?

Is een dergelijke vorm van overerving in het algemeen (het vergroten van de zichtbaarheid van bepaalde methoden als de enige wijziging) een bekende praktijk en wat zijn enkele scenario’s waarin het nuttig kan zijn (ervan uitgaande dat we toegang hebben tot alle code)?


Antwoord 1, autoriteit 100%

In LiveData – Documentatie voor Android-ontwikkelaarskunt u dat zien voor LiveData, setValue()& postValue()methoden zijn niet openbaar.

Terwijl u in MutableLiveData – Documentatie voor Android-ontwikkelaarskunt zien dat MutableLiveDataLiveDataintern uitbreidt en ook de twee magische methoden van LiveDatazijn openbaarhierin beschikbaar en ze zijn setValue()& postValue().

setValue(): stel de waarde in en verzend de waarde naar alle actieve waarnemers, moet worden aangeroepen vanuit hoofdthread.

postValue(): post een taak naar de hoofdthread om de waarde te overschrijven die is ingesteld door setValue(), moet worden aangeroepen vanuit achtergrondthread.

Dus LiveDatais onveranderlijk. MutableLiveDatais LiveDatawat mutableis & draadveilig.


Antwoord 2, autoriteit 10%

Dit is het hele bestand 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);
    }
}

Dus ja, het verschil komt alleen door postValueen setValueopenbaar te maken.

Een use case die ik me uit mijn hoofd kan herinneren, is voor inkapseling met Ondersteunend onroerend goedin Kotlin.
Je kunt LiveDatablootstellen aan je Fragment/Activity (UI Controller), ook al kun je MutableLiveDatavoor manipulatie hebben in je ViewModel-klasse.

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

Op deze manier kan uw UI-controller alleen waarden observeren zonder deze te kunnen bewerken. Het is duidelijk dat uw UI-controller waarden kan bewerken met behulp van openbare methoden van TempViewModelzoals incrementCount().

Opmerking: om veranderlijke/onveranderlijke verwarring te verduidelijken –

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
}

Antwoord 3, autoriteit 2%

MutableLiveData breidt uit van LiveData. De beveiligde methoden van LiveData kunnen alleen worden geadresseerd door zelf of subklassen. Dus in dit geval heeft MutableLiveData, een subklasse van LiveData, toegang tot deze beschermde methoden.

Wat u zou willen doen, is een instantie observeren en kijken of er wijzigingen zijn. Maar tegelijkertijd wil je niet dat “buitenstaanders” die instantie die je waarneemt veranderen. In zekere zin creëert dit een probleem, aangezien je een object wilt hebben dat wel en veranderbaar is, om elke nieuwe status bij te werken, en niet veranderlijk, om ervoor te zorgen dat niemand die dat niet zou moeten doen, deze instantie kan updaten. Deze twee functies zijn in conflict met elkaar, maar kunnen worden opgelost door een extra laag te maken.

Dus wat je doet is je klasse, LiveData, uitbreiden met een klasse die toegang heeft tot zijn methoden. De sublaag, in dit geval MutableLiveData, heeft toegang tot de beveiligde methoden van zijn bovenliggende (/super).

Nu begint u met het maken van instanties en maakt u uw waarnemerinstantie van MutableLiveData. Tegelijkertijd maakt u een LiveData-instantie die naar dezelfde instantie verwijst. Omdat MutableLiveData LiveData uitbreidt, is elke MutableLiveData-instantie een LiveData-object en kan daarom worden verwezen door een LiveData-variabele.

Nu is de truc bijna klaar. U stelt alleen de LiveData-instantie bloot, niemand kan de beschermde methoden gebruiken, noch kan deze naar zijn super casten (misschien tijdens het compileren, maar het zou niet worden uitgevoerd: RunTime-fout). En u houdt de feitelijke instantie van de subklasse privé, zodat deze alleen kan worden gewijzigd door degenen die eigenaar zijn van de instantie, met behulp van de methoden van de instantie.

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

Nu meldt de superklasse wanneer er wijzigingen zijn aangebracht.

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

Blokcitaat
Is in het algemeen zo’n vorm van overerving (het vergroten van de zichtbaarheid van bepaalde methoden als de enige verandering) een bekende praktijk en wat zijn enkele scenario’s waarin het nuttig kan zijn (ervan uitgaande dat we toegang hebben tot alle code)?

Ja, het is vrij bekend en dit hierboven beschreven scenario is een veelvoorkomend scenario. Verwijder het waarnemerspatroon en maak het gewoon in een set / get-vorm, zou er net zo veel baat bij hebben. Afhankelijk van waar je het implementeert, uiteindelijk geen gouden regels.

Other episodes