web-dev-qa-db-fra.com

Nullabilité et LiveData avec Kotlin

Je veux utiliser LiveData avec Kotlin et avoir des valeurs qui ne devraient pas être nulles. Comment gérez-vous cela? Peut-être un wrapper autour de LiveData? Recherche de bons modèles ici .. À titre d'exemple:

class NetworkDefinitionProvider : MutableLiveData<NetworkDefinition>() {
    val allDefinitions = mutableListOf(RinkebyNetworkDefinition(), MainnetNetworkDefinition(), RopstenNetworkDefinition())

    init {
        value = allDefinitions.first()
    }

    fun setCurrent(value: NetworkDefinition) {
        setValue(value)
    }
}

Je sais que la valeur ne sera pas nulle lors de l'accès - mais je devrai toujours vérifier la valeur nulle ou avoir ces vilains !!.

14
ligi

J'ai créé une propriété d'extension. Ce n'est pas super joli, mais c'est est assez simple.

val <T> LiveData<T>.valueNN
    get() = this.value!!

Usage

spinner.loading = myLiveData.valueNN.homeState.loading

Je ne suis pas convaincu d'avoir ajouté "NN" comme bonne convention de dénomination, mais cela dépasse la portée de la question :)

1
David Whitman

J'améliore peu réponse The Lucky Coder. Cette implémentation ne peut pas accepter du tout les valeurs nulles.

class NonNullMutableLiveData<T: Any>(initValue: T): MutableLiveData<T>() {

    init {
        value = initValue
    }

    override fun getValue(): T {
        return super.getValue()!!
    }

    override fun setValue(value: T) {
        super.setValue(value)
    }

    fun observe(owner: LifecycleOwner, body: (T) -> Unit) {
        observe(owner, Observer<T> { t -> body(t!!) })
    }

    override fun postValue(value: T) {
        super.postValue(value)
    }    
}
12
icebail

Je ne sais pas si c'est la meilleure solution mais c'est ce que j'ai trouvé et ce que j'utilise:

class NonNullLiveData<T>(private val defaultValue: T) : MutableLiveData<T>() {

    override fun getValue(): T = super.getValue() ?: defaultValue

    fun observe(owner: LifecycleOwner, body: (T) -> Unit) {
        observe(owner, Observer<T> {
            body(it ?: defaultValue)
        })
    }
}

Création du champ:

val string = NonNullLiveData("")

Et en l'observant:

viewModel.string.observe(this) {
    // Do someting with the data
}
9
The Lucky Coder

Vous pouvez créer une extension pour LifecycleOwner

fun <T> LifecycleOwner.observe(liveData: LiveData<T?>, lambda: (T) -> Unit) {
    liveData.observe(this, Observer { if (it != null) lambda(it) })
}

puis dans votre fragment/activité

observe(liveData) { ... }
2
Filip P.

Tu peux le faire

normalLiveData
  .nonNull()
  .observe(this, { result -> 
    // result is non null now
  })

Il y a un article à ce sujet. https://medium.com/@henrytao/nonnull-livedata-with-kotlin-extension-26963ffd03

1
Henry Tao

J'ai vraiment aimé The Lucky Coder 's answer. En ce qui concerne le danger d'essayer de définir une valeur nulle, je pense que nous devrions lever une exception pour cela (similaire à ce que David Whitman pointait):

class NonNullLiveData<T>(private val defaultValue: T) : MutableLiveData<T>() {

override fun getValue(): T = super.getValue() ?: defaultValue

override fun setValue(value: T) {
    if(value != null) {
        super.setValue(value)
    }
    else throw IllegalArgumentException("Cannot set a null value to this Type. Use normal MutableLiveData instead for that.")
}

override fun postValue(value: T) {
    if(value != null) {
        super.postValue(value)
    }
    else throw IllegalArgumentException("Cannot post a null value to this Type. Use normal MutableLiveData instead for that.")
}

fun observe(owner: LifecycleOwner, body: (T) -> Unit) {
    observe(owner, Observer<T> {
        body(it ?: defaultValue)
    })
}

}

Désormais, la valeur dans MutableLiveData ne sera jamais nulle, de sorte que la classe ne sera pas utilisée d'une manière qui n'est pas prévue.

0
Ignacio Garcia

L'utilisation de chaînes est simple en utilisant cette méthode:

val someLiveData = MutableLiveData<String>()
...
someLiveData.value.orEmpty()

Et vous obtiendrez la valeur réelle si elle est définie ou une chaîne vide au lieu de null.

0
Vladimir Petrovski

C'est ma solution.

class NotNullLiveData<T : Any>
constructor(private val default: T) : MediatorLiveData<T>() {
    override fun getValue(): T {
        return super.getValue() ?: default
    }

    override fun setValue(value: T) {
        super.setValue(value)
    }
}

@MainThread
fun <T : Any> mutableLiveDataOfNotNull(initValue: T): NotNullLiveData<T> {
    val liveData = NotNullLiveData(initValue)
    liveData.value = initValue
    return liveData
}

@MainThread
fun <T> mutableLiveDataOf(initValue: T): MutableLiveData<T> {
    val liveData = MutableLiveData<T>()
    liveData.value = initValue
    return liveData
}


fun <T : Any> LiveData<T?>.toNotNull(default: T): NotNullLiveData<T> {
    val result = NotNullLiveData(default)
    result.addSource(this) {
        result.value = it ?: default
    }
    return result
}
0
ipcjs