Cuando usamos programación reactiva/ funcional y usamos los observadores de datos, pueda que necesitamos que el observador al recibir los datos no vuelva a recibirlos si se rota el dispositivo, se puede usar para informar el usuario de algo, para obtener ese resultado debemos implementar el patrón SingleLiveEvent

open class SingleLiveEvent<out T>(private val content: T) {
var hasBeenHandled = false
private set // Allow external read but not write
/**
* Returns the content and prevents its use again.
*/
fun getContentIfNotHandled(): T? {
return if (hasBeenHandled) {
null
} else {
hasBeenHandled = true
content
}
}
/**
* Returns the content, even if it's already been handled.
*/
fun peekContent(): T = content
}
Con ese patrón puede existir varios observadores al mismo tiempo, en cambio si solo queremos que persista un solo observador de los eventos usar la solución original de Google SingleLiveEvent
Declaración en ViewModel
Dentro del viewmodel debemos implementar el SingleLiveEvent
//Notify Single SingleLiveEvent
private val _notifyEvent = MutableLiveData<SingleLiveEvent<String>>()
val notifyEvent: LiveData<SingleLiveEvent<String>>
get() = _notifyEvent
Observador del SingleLiveEvent
mViewModel.notifyEvent.observe(this, Observer { it ->
it.getContentIfNotHandled()?.let {
Toast.makeText(this, it, Toast.LENGTH_SHORT).show()
}
})
Notificar un Evento
Para notificar un evento solo es necesario cambiar el valor de la variable que hemos declarado con SingleLiveEvent en este caso _notifyEvent
_notifyEvent.value = SingleLiveEvent("tarea finalizada")
_notifyEvent.postValue(SingleLiveEvent("procesando datos"))
Más información al articulo original LiveData with SnackBar, Navigation and other events (the SingleLiveEvent case)
Multiples observadores
class SingleLiveEvent2<T> : MutableLiveData<T>() {
private val pending = AtomicBoolean(false)
private val observers = mutableSetOf<Observer<T>>()
private val internalObserver = Observer<T> { t ->
if (pending.compareAndSet(true, false)) {
observers.forEach { observer ->
observer.onChanged(t)
}
}
}
@MainThread
override fun observe(owner: LifecycleOwner, observer: Observer<T>) {
observers.add(observer)
if (!hasObservers()) {
super.observe(owner, internalObserver)
}
}
override fun removeObservers(owner: LifecycleOwner) {
observers.clear()
super.removeObservers(owner)
}
override fun removeObserver(observer: Observer<T>) {
observers.remove(observer)
super.removeObserver(observer)
}
@MainThread
override fun setValue(t: T?) {
pending.set(true)
super.setValue(t)
}
@MainThread
fun call() {
value = null
}
}
val event = SingleLiveEvent2<String>()
// Both Observer is working
viewModel.event.observe(this, Observer {
// ...
})
viewModel.event.observe(this, Observer {
// ...
})
Extraido de: https://star-zero.medium.com/singleliveevent-livedata-with-multi-observers-384e17c60a16