Patrón SingleLiveEvent para ViewModel en Kotlin y AndroidX


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

Publicado por Codelaby

Mobile DevDesigner

Deja una respuesta

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Salir /  Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Salir /  Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Salir /  Cambiar )

Conectando a %s

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.

A %d blogueros les gusta esto: