Pourquoi existe-t-il une sous-classe MutableLiveData distincte de LiveData?

96

Il semble que cela MutableLiveDatadiffère du LiveDataseul fait de rendre publiques les méthodes setValue()et postValue(), alors LiveDataqu'elles sont protégées.

Quelles sont les raisons de créer une classe distincte pour ce changement et de ne pas simplement définir ces méthodes comme publiques en LiveDatasoi?

D'une manière générale, une telle forme d'héritage (l'augmentation de la visibilité de certaines méthodes étant le seul changement) est-elle une pratique bien connue et quels sont certains scénarios où elle peut être utile (en supposant que nous ayons accès à tout le code)?

Alexandre Kulyakhtin
la source
10
c'est une décision de conception. LiveDataest immuable, puisque le client ne peut pas changer l'état interne, donc thread-safe
Blackbelt

Réponses:

138

Dans LiveData - Documentation pour les développeurs Android , vous pouvez voir que pour LiveData, les méthodes setValue()& postValue()ne sont pas publiques.

Alors que, dans MutableLiveData - Documentation pour les développeurs Android , vous pouvez voir que, MutableLiveDatas'étend en LiveDatainterne et que les deux méthodes magiques de LiveDatasont disponibles publiquement dans ce document et qu'elles sont setValue()& postValue().

setValue(): définir la valeur et distribuer la valeur à tous les observateurs actifs, doit être appelée à partir du thread principal .

postValue(): publier une tâche sur le thread principal pour remplacer la valeur définie par setValue(), doit être appelée à partir du thread d'arrière-plan .

Donc, LiveDatac'est immuable . MutableLiveDataest LiveDataqui est mutable et thread-safe .

Sneh Pandya
la source
36
Ce n'est pas vraiment que LiveData est immuable, mais simplement qu'il ne peut pas être modifié en dehors de la classe ViewModel. La classe ViewModel peut le modifier comme bon lui semble (par exemple un minuteur ViewModel). Vous utiliseriez MutableLiveData si vous vouliez le modifier en dehors de la classe ViewModel.
Elliptica
2
Prenons ce scénario, une application avec le modèle de référentiel (Server + Room) où Room est la source unique de vérité. L'application obtient les données uniquement de Room, tandis que Room obtient sa mise à jour depuis le serveur. Est-ce que mutableLiveData doit être utilisé parce que les données du serveur de mise à jour Room ou LiveData peuvent être utilisées?
Dr4ke the b4dass
5
LiveData est abstrait, vous ne pouvez donc pas créer directement un objet LiveData sans l'étendre. MutableLiveData étend LiveData.
Serdar Samancıoğlu
1
Liens vers LiveData et MutableLiveData directement vers la documentation obsolète. Pourquoi lorsque j'ai suggéré une modification avec des liens réels, elle a été rejetée?
Daniel
1
@Daniel ne sait pas pourquoi il a été rejeté par d'autres critiques dans la file d'attente de révision. J'ai approuvé le changement, merci! :)
Sneh Pandya
10

Voici le MutableLiveData.javafichier complet :

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

Alors oui, la différence ne vient que du rendu postValueet du setValuepublic.

Un cas d'utilisation dont je peux me souvenir de ma tête est l'encapsulation à l'aide de Backing Property dans Kotlin. Vous pouvez exposer LiveDataà votre fragment / activité (contrôleur d'interface utilisateur) même si vous pouvez avoir MutableLiveDatapour manipulation dans votre ViewModelclasse.

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

De cette façon, votre contrôleur d'interface utilisateur ne pourra observer que les valeurs sans pouvoir les modifier. De toute évidence, votre contrôleur d'interface utilisateur peut modifier les valeurs à l'aide de méthodes publiques TempViewModelsimilaires incrementCount().

Remarque : pour clarifier la confusion mutable / immuable -

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
}
Ananthe
la source
C'est quoi _score?
IgorGanapolsky
0

MutableLiveData est une extension de LiveData. Les méthodes protégées de LiveData ne peuvent être adressées que par soi-même ou par des sous-classes. Donc, dans ce cas, MutableLiveData étant une sous-classe de LiveData peut accéder à ces méthodes protégées.

Ce que vous aimeriez faire, c'est observer sur une instance et voir s'il y a des changements. Mais en même temps, vous ne voulez pas que des "étrangers" modifient cette instance que vous observez. Dans un sens, cela crée un problème, car vous aimeriez avoir un objet qui soit et modifiable, pour mettre à jour tout nouveau statut, et non modifiable, pour vous assurer que personne qui ne devrait pas peut mettre à jour cette instance. Ces deux fonctionnalités sont en conflit l'une avec l'autre, mais peuvent être résolues en créant une couche supplémentaire.

Donc, ce que vous faites est d'étendre votre classe, LiveData, avec une classe qui peut accéder à ses méthodes. La sous-couche, dans ce cas MutableLiveData, est capable d'accéder aux méthodes protégées de son parent (/ super).

Vous commencez maintenant à créer des instances et créez votre instance d'observateur de MutableLiveData. En même temps, vous créez une instance LiveData faisant référence à cette même instance. Comme MutableLiveData étend LiveData, toute instance de MutableLiveData est un objet LiveData et peut donc être référencée par une variable LiveData.

Maintenant, le tour est presque terminé. Vous exposez uniquement l'instance LiveData, personne ne peut utiliser ses méthodes protégées, ni ne peut la convertir en son super (peut-être au moment de la compilation, mais elle ne fonctionnerait pas: erreur RunTime). Et vous gardez l'instance de sous-classe réelle privée, de sorte qu'elle ne peut être modifiée que par ceux qui possèdent l'instance, en utilisant les méthodes de l'instance.

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

Désormais, la super classe avertit lorsque des modifications sont appliquées.

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

Blockquote D'une manière générale, une telle forme d'héritage (l'augmentation de la visibilité de certaines méthodes étant le seul changement) est-elle une pratique bien connue et quels sont certains scénarios où cela peut être utile (en supposant que nous ayons accès à tout le code)?

Oui, c'est assez connu et cela décrit ci-dessus est un scénario courant. Supprimez le modèle d'observateur et créez-le simplement sous une forme set / get en bénéficierait tout autant. Selon ofc où vous l'implémentez, pas de règles d'or à la fin.

Khamaseen
la source