Android LiveData-Ereignisse

LiveData ist ein großartiges Tool zum Verknüpfen des Status Ihrer Daten und Objekte mit einem Lebenszyklus (LifecycleOwner, normalerweise ein Fragment oder eine Aktivität).

In der Regel werden LiveData im ViewModel abgelegt und zum Aktualisieren des Status Ihrer Benutzeroberfläche verwendet. Oft kann ein ViewModel einen LifecycleOwner überleben und einen LiveData-Status beibehalten. Ein solcher Mechanismus eignet sich, wenn Sie Daten speichern und nach einiger Zeit wiederherstellen müssen, z. B. nach einer Konfigurationsänderung.

Aber was ist, wenn wir den Mechanismus von Ereignissen nutzen wollen, nicht Staaten? Und es ist im Kontext des Browser-Lebenszyklus (LifecycleOwner) notwendig. Beispielsweise müssen wir nach einem asynchronen Vorgang eine Nachricht anzeigen, vorausgesetzt, LifecycleOwner ist noch aktiv, verfügt über aktive Browser und ist bereit, die Benutzeroberfläche zu aktualisieren. Wenn wir LiveData verwenden, erhalten wir nach jeder Konfigurationsänderung oder mit jedem neuen Abonnenten dieselbe Nachricht. Eine der Lösungen, die sich nach der Verarbeitung der Daten in einem Browser anbietet, besteht darin, diese Daten auf LiveData zurückzusetzen.

Zum Beispiel ein Code wie dieser:

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

Dieser Ansatz hat jedoch mehrere Nachteile und erfüllt nicht alle erforderlichen Anforderungen.

Ich hätte gerne einen Ereignismechanismus, der:

  1. benachrichtigt nur aktive Teilnehmer
  2. zum Zeitpunkt des Abonnements nicht über vorherige Daten benachrichtigt,
  3. hat die Möglichkeit, das behandelte Flag auf true zu setzen, um die weitere Verarbeitung des Ereignisses zu unterbrechen.

Ich habe die MutableLiveEvent-Klasse implementiert, die alle oben genannten Eigenschaften aufweist und wie normale LiveData funktionieren kann.

Wie man benutzt:

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

Der gesamte Code ist auf GitHub verfügbar, und im Folgenden werde ich ein wenig über die Implementierung sprechen.

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

Die Idee ist, dass innerhalb der MutableLiveEvent-Klasse in den Observ- und ObservForever-Methoden Browser in eine spezielle interne Klasse PendingObserver eingeschlossen werden, die den realen Browser nur einmal und nur dann aufruft, wenn das ausstehende Flag auf true gesetzt ist und das Ereignis noch nicht verarbeitet wurde.

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

In PendingObserver ist das ausstehende Flag standardmäßig auf false gesetzt. Dies löst Punkt 2 (keine Benachrichtigung über alte Daten) aus unserer Liste.

Und der Code in MutableLiveEvent

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

Setzt zuerst anstehend auf true und aktualisiert erst dann die Daten in sich. Dies stellt die Umsetzung von Anspruch 1 sicher. (Nur aktive Teilnehmer benachrichtigen).

Der letzte Punkt, über den ich noch nicht gesprochen habe, ist EventArgs. Diese Klasse ist eine Verallgemeinerung, bei der ein behandeltes Flag vorhanden ist, um die weitere Verarbeitung des Ereignisses zu unterbrechen (Abschnitt 3).

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

Das ist alles, danke fürs Zuschauen!

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


All Articles