6
votes

Comment fonctionne la syntaxe Android LiveData get ()?

Je comprends le besoin de créer des points getter et setter pour LiveData dans le ViewModel, mais je Je cherche à comprendre comment la syntaxe get () fonctionne sous Android.

ie:

val isRealtime: LiveData<Boolean>
    get() = _isRealtime
private val _isRealtime = MutableLiveData<Boolean>()


2 commentaires

Vous avez déjà essayé de lire la documentation de Kotlin sur les fonctions?


Oui, mais ça fait quelques années. Je vais vérifier ça. Merci pour la pointe @MarcinOrlowski!


3 Réponses :


10
votes

get () n'est pas lié à Android.

private val _isRealtime = MutableLiveData<Boolean>()
val isRealtime: LiveData<Boolean> = _isRealtime

Ici, get () remplace le getter Kotlin généré automatiquement fonction pour la propriété isRealtime . Donc, au lieu de renvoyer sa propre valeur, il renvoie la valeur de _isRealtime.

Personnellement, je recommande une syntaxe plus simple:

val isRealtime: LiveData<Boolean>
    get() = _isRealtime

L'objectif de l'un ou l'autre de ces éléments est de garder la mutabilité privée, afin que les consommateurs de cette classe ne mettent pas accidentellement à jour le MutableLiveData eux-mêmes.


3 commentaires

Merci d'avoir partagé la syntaxe plus simple @CommonsWare! Il est bon de savoir que je n'ai pas besoin d'indiquer explicitement get () .


@AdamHurwitz: get () a ses utilisations, mais pour des scénarios plus complexes. Par exemple, un modèle dans Android consiste à créer des propriétés d'extension privées sur Intent pour les extras ou sur Bundle pour les arguments de fragment. Là, vous remplaceriez get () et set () sur les propriétés de l'extension pour acheminer vers les getters / setters appropriés (par exemple, getString () et putString () sur un Bundle ). Pour un cas simple comme celui-ci, cependant, cela semble exagéré.


En outre, un avantage organisationnel de conserver get () est qu'il vous permet de définir toutes les variables publiques en haut de la classe ViewModel .



2
votes

J'ai écrit une fonction util pour cette logique:

private val _counter = MutableLiveData<Int>()    
val counter by immutable(_counter)

Ensuite, vous pouvez utiliser dans l'un de vos ViewModel comme:

private val _counter: MutableLiveData<Int> = MutableLiveData()    
val counter: LiveData<Int> by immutable(_counter)

ou en bref:

import android.arch.lifecycle.LiveData
import android.arch.lifecycle.MutableLiveData
import kotlin.reflect.KProperty

fun <T> immutable(data: MutableLiveData<T>): Immutable<T> {
    return Immutable(data)
}

class Immutable<T>(private val data: MutableLiveData<T>) {
    operator fun getValue(thisRef: Any?, property: KProperty<*>): LiveData<T> {
        return data
    }
}


0 commentaires

2
votes

Dans Kotlin, nous avons plusieurs façons d'exposer les données en direct de ViewModel à la vue.

class MyViewModel: ViewModel() {

    // Solution 1 - make MutableLiveData public
    // This approach works, but this is a bad idea because
    // view can modify the LiveData values
    val liveDataA1 = MutableLiveData<State>()

    // Solution 2 - let's make LiveData public (expose it instead of MutableLiveData)
    // Now from view perspective this solution looks fine, bu we have a problem,
    // because we need MutableLiveData within ViewModel to put/post new values to
    // the stream (we can't post values to LiveData).
    val liveDataA2 = MutableLiveData<State>() as LiveData<State>

    // Let's capture our requirements:
    // 1. We need to expose (immutable) LiveData to the view,
    // so it cannot edit the data itself.
    // 2. We need to access MutableLiveData from ViewModel to put/post new values.
    // Now, let's consider few appropriate solutions

    // Solution 3
    // Let's name mutable live data using underscore prefix
    private val _liveData3 = MutableLiveData<State>()
    val liveData3 = _liveData3 as LiveData<State>

    // Solution 4
    // We can also perform casting by specifying type for a variable
    // (we can do it because MutableLiveData extends LiveData)
    private val _liveData4 = MutableLiveData<State>()
    val liveData4: LiveData<State> = _liveData4

    // Solution 5
    // Starting from Kotlin 1.4-M.2 we can delegate call to another property
    private val _liveData5 = MutableLiveData<State>()
    val liveData5 by this::_liveData5

    // Solution 6
    // These above solutions work quite well, but we could do even better by
    // defining custom asLiveData extension function.
    private val _liveData6 = MutableLiveData<State>()
    val liveData6 = _liveData6.asLiveData()

    fun <T> MutableLiveData<T>.asLiveData() = this as LiveData<T>
    // Amount of code is similar, but notice that this approach works much better
    // with code completion.

    // Solution 7 (IMO Best)
    // We can also use alternative naming convention - use "mutableLiveData"
    // as variable for mutable live data instead of using underscore prefix
    private val mutableLiveData7 = MutableLiveData<State>()
    val liveData7 = mutableLiveData7.asLiveData()

    // BTW
    // We could also expose getLiveData8() method, but liveData is a state not an action.

    // Solution 9
    // This does not create backing field for the property
    // (more optimised but still Solution 7 is easier to use)
    private val _liveData9 = MutableLiveData<State>()
    val liveData9 get() = _liveData9 as LiveData<State>
}


2 commentaires

J'aime vraiment la solution n ° 5. Malheureusement, je ne peux pas encore l'utiliser sur Android. BTW, je pense que this :: _ liveData5 peut en fait être simplement liveData5


Non, ça ne peut pas. D'une manière ou d'une autre, le this :: est nécessaire