Événements Android LiveData

LiveData est un excellent outil pour relier l'état de vos données et objets à un cycle de vie (LifecycleOwner, généralement un fragment ou une activité).

En règle générale, LiveData est placé dans le ViewModel et utilisé pour mettre à jour l'état de votre interface utilisateur. Souvent, un ViewModel peut survivre à un LifecycleOwner et conserver un état LiveData. Un tel mécanisme convient lorsque vous devez enregistrer des données et les restaurer après un certain temps, par exemple, après un changement de configuration.

Mais que se passe-t-il si nous voulons utiliser le mécanisme des événements, pas les États? Et cela est nécessaire dans le contexte du cycle de vie du navigateur (LifecycleOwner). Par exemple, nous devons afficher un message après une opération asynchrone, à condition que LifecycleOwner soit toujours en vie, dispose de navigateurs actifs et soit prêt à mettre à jour son interface utilisateur. Si nous utilisons LiveData, nous recevrons le même message après chaque changement de configuration, ou avec chaque nouvel abonné. L'une des solutions qui se propose, après avoir traité les données dans un navigateur, est de réinitialiser ces données sur LiveData.

Par exemple, un code comme celui-ci:

Observer { handle(it) yourViewModel.liveData.value = null } 

Mais cette approche présente plusieurs inconvénients et ne répond pas à toutes les exigences nécessaires.

J'aimerais avoir un mécanisme d'événement qui:

  1. avertit uniquement les abonnés actifs
  2. au moment de la souscription ne notifie pas les données précédentes,
  3. a la possibilité de définir l'indicateur géré sur true pour interrompre le traitement ultérieur de l'événement.

J'ai implémenté la classe MutableLiveEvent, qui possède toutes les propriétés ci-dessus et qui peut fonctionner comme un LiveData normal.

Comment utiliser:

 //   EventArgs        class MyIntEventArgs(data: Int) : EventArgs<Int>(data) //  viewModel class MainViewModel : ViewModel() { private val myEventMutable = MutableLiveEvent<MyIntEventArgs>() val myEvent = myEventMutable as LiveData<MyIntEventArgs> fun sendEvent(data: Int) { myEventMutable.value = MyIntEventArgs(data) } } val vm = ViewModelProviders.of(this).get(MainViewModel::class.java) vm.myEvent.observe(this, Observer { //   /* *   ,    , *      ,   handled = true */ it.handled = true }) 

Tout le code est disponible sur GitHub , et ci-dessous je parlerai un peu de l'implémentation.

 class MutableLiveEvent<T : EventArgs<Any>> : MutableLiveData<T>() { internal val observers = ArraySet<PendingObserver<in T>>() @MainThread override fun observe(owner: LifecycleOwner, observer: Observer<in T>) { val wrapper = PendingObserver(observer) observers.add(wrapper) super.observe(owner, wrapper) } override fun observeForever(observer: Observer<in T>) { val wrapper = PendingObserver(observer) observers.add(wrapper) super.observeForever(observer) } @MainThread override fun removeObserver(observer: Observer<in T>) { when (observer) { is PendingObserver -> { observers.remove(observer) super.removeObserver(observer) } else -> { val pendingObserver = observers.firstOrNull { it.wrappedObserver == observer } if (pendingObserver != null) { observers.remove(pendingObserver) super.removeObserver(pendingObserver) } } } } @MainThread override fun setValue(event: T?) { observers.forEach { it.awaitValue() } super.setValue(event) } } 

L'idée est qu'à l'intérieur de la classe MutableLiveEvent, dans les méthodes observe et observeForever, encapsule les navigateurs dans une classe interne spéciale PendingObserver, qui n'appelle le navigateur réel qu'une seule fois et uniquement si l'indicateur en attente est défini sur true et que l'événement n'a pas encore été traité.

 internal class PendingObserver<T : EventArgs<Any>>(val wrappedObserver: Observer<in T>) : Observer<T> { private var pending = false override fun onChanged(event: T?) { if (pending && event?.handled != true) { pending = false wrappedObserver.onChanged(event) } } fun awaitValue() { pending = true } } 

Dans PendingObserver, l'indicateur en attente est défini sur false par défaut. Cela résout le point 2 (ne pas notifier les anciennes données) de notre liste.

Et le code dans MutableLiveEvent

 override fun setValue(event: T?) { observers.forEach { it.awaitValue() } super.setValue(event) } 

Définit d'abord l'attente sur true puis met à jour les données en elle-même. Cela garantit la mise en œuvre de la revendication 1. (alerter uniquement les abonnés actifs).

Le dernier point dont je n'ai pas encore parlé est EventArgs. Cette classe est une généralisation dans laquelle il existe un indicateur géré pour interrompre le traitement ultérieur de l'événement (article 3).

 open class EventArgs<out T>(private val content: T?) { var handled: Boolean = false val data: T? get() { return if (handled) { null } else { content } } } 

C'est tout, merci d'avoir regardé!

Source: https://habr.com/ru/post/fr468749/


All Articles