Programación Reactiva: Qué es, para qué sirve y cómo usarla en Android
Antonio Leiva

La programación reactiva es un concepto que se ha popularizado en los últimos años, especialmente en Android, gracias a la aparición de Frameworks como RxJava.

La realidad es que esta idea ya lleva mucho tiempo dando vueltas, y es mucho más sencilla de lo que parece.

Hoy te quiero desmitificar esta idea, que comprendas qué es exactamente la programación reactiva, y qué problemas te suele solucionar en el día a día.

Este artículo forma parte de una serie en la que te voy a contar cómo puedes iniciarte con Flow, la apuesta de Kotlin por la programación reactiva, que además se integra con las corrutinas.

Si no quieres perderte nada, te animo a que te suscribas a mi canal de YouTube, donde irás estando al día de los vídeos publicados.

Qué es la programación reactiva

En la programación clásica, todo se basa en acciones que nosotros realizamos activamente:

  • Si queremos algo de la base de datos
  • Si necesitamos algo de un servidor
  • Si queremos recoger información de un sensor del dispositivo
  • Si estamos usando la localización del usuario

Lo que hacemos en cada caso es ir y preguntarle “oye, ¿qué datos tienes para mí ahora mismo?

En la programación reactiva, le damos la vuelta a la idea. La forma de trabajar sería “estoy interesado en tus datos, cada vez que haya un cambio infórmame”.

Nos encontramos entonces con un software que se actualiza solo cuando las fuentes de datos de las que dependen se modifican.

Esto es especialmente interesante en interfaces de usuario, ya que muchas veces necesitamos actualizaciones en tiempo real de la información que estamos viendo, y muchas veces va a ser difícil descubrir cuándo algo se ha modificado.

Tradicionalmente se han hecho muchas “chapuzas” para solucionar estas situaciones, como hacer peticiones cada x tiempo para comprobar si ha habido cambios, pero la programación reactiva permite hacer esto de forma mucho más sencilla.

Patrón Observer

¿Qué tal si en vez de pedir los datos, se nos informara cada vez que han cambiado? La programación reactiva, simplificando mucho, no es más que la implementación del patrón Observer.

En este patrón, tenemos un objeto Observable al que podemos suscribirnos, y cada vez que haya un cambio en su valor seremos informados.

Programación Reactiva - Patrón Observer
By Gregorybleiker – Own work, CC BY-SA 4.0

Actores en la programación reactiva

Por tanto, podríamos decir que tenemos los siguientes actores en la programación reactiva:

Flujos de datos

Son datos que se van modificando cada cierto tiempo, y que puede tener interés observar en tiempo real.

  • En una base de datos o un servidor, cada vez que se modifica cierto valor
  • En un sensor, cada vez que el valor del sensor cambia
  • En la localización, cada vez que el usuario se mueve y el punto de localización cambia

Observable

Un componente que puede ser observado, y que se encargará de informar cuando ese flujo de datos se modifique.

Normalmente este y el anterior están unidos, pero quiero dejar estos conceptos separados de momento.

Observer (u observador)

Es el elemento que observa esas modificaciones. Cuando el dato se modifica, se notifica al componente que lo está utilizando.

Normalmente este observer necesita suscribirse cuando quiere empezar a recibir datos, y desuscribirse cuando ya no le interesan más esos datos.

¿Y ya está?

Bueno, a grandes rasgos sí, pero la realidad es un poco más compleja.

La cosa se complica cuando tenemos distintos flujos de datos, queremos combinarlos mediante distintas operaciones, y hacer que ciertas cosas se ejecuten en unos hilos y otras en otros.

A veces la generación de eso datos requiere de operaciones complejas, o que van a bloquear mucho tiempo el hilo principal, y es importante tener una forma sencilla de no liarla en este aspecto.

Así que podría decirse que podemos tener un par de elementos más, que no son indispensables pero que son muy útiles

Dispatchers o Schedulers

En cada framework de programación reactiva se llaman de una forma distinta, pero básicamente son un conjunto de hilos donde se van a ejecutar las operaciones, y unas reglas que optimizan su uso para ser lo más eficientes posibles.

Cada operación se ejecutará en el que le digamos, para de esa forma liberar el hilo principal y optimizar recursos. Suele haber 3 tipos:

  • UI: Es el hilo donde se pinta la interfaz de usuario
  • I/O: para operaciones de entrada/salida que no requieren de trabajo del procesador: esperar que responda una base de datos, un servidor, un sensor, el sistema de archivos…
  • Computación: para tareas que requieren del uso intensivo de la CPU. Es decir, algoritmos propios del programa.

Si has usado corrutinas, seguramente todo esto te suena ya. Por orden serían Dispatchers.Main, Dispatchers.IO y Dispatchers.Default.

Había personas que solo por esta funcionalidad ya usaban RxJava, pero era un poco como matar moscas a cañonazos, porque como ves no es la parte importante de la programación reactiva, es solo un añadido para simplificar las cosas.

Operadores sobre flujos de datos

Aquí es donde la cosa se suele complicar. Existen montones de operadores que nos permiten modificar y combinar flujos de datos, y conocerlos todos es muy complicado.

En RxJava, esto suele ser lo que mayor curva de aprendizaje conlleva.

Por suerte, con Flow los operadores son los mismos que los que usamos en las colecciones de Kotlin, por lo que es bastante más sencillo de aprender. Aquí de momento no vamos a entrar mucho.

Vamos a crear nuestro propio sistema de programación reactiva

Sí, como lo oyes. En este primer artículo no vamos a usar ningún framework, sino que lo vamos a crear nosotros mismos. Realmente te hablaré de uno que igual no te esperas al final, pero no nos adelantemos.

Imaginemos que tenemos una serie de datos que se van generando de forma automática, y que tenemos que actualizar un RecyclerView cada vez que ese dato cambia.

Lo primero que necesitaremos es crearnos una estructura de datos que nos permita observar un dato y cuándo se modifica:

class Observable<T>(var value: T) {
    private var observers: MutableList<(T) -> Unit> = mutableListOf()

    fun subscribe(observer: (T) -> Unit) {
        observers.add(observer)
        observer(value)
    }

    fun unsubscribe(observer: (T) -> Unit) {
        observers.remove(observer)
    }

    fun updateValue(newValue: T) {
        value = newValue
        notifyObservers()
    }

    private fun notifyObservers() {
        observers.forEach { it(value) }
    }
}

Las partes importantes son que hay un dato, un listado de observers, y unas funciones para suscribirse, desuscribirse, y actualizar el valor.

Si te pierdes un poco con el código Kotlin que hay aquí, te animo a que te apuntes a mi training gratuito para que descubras cómo empezar, cómo sacarle el máximo partido al lenguaje, y por qué necesitas aprenderlo si eres desarrollador/a Android.

Haz click aquí para unirte ahora.

Luego creamos un objeto que contenga el observable y que permita empezar a emitir valores. Esto se lanzará en el Application a modo de ejemplo:

object ItemsProvider {
    val observable = Observable<List<String>>(emptyList())
    private var values = emptyList<String>()
    private val random = Random(System.currentTimeMillis())

    fun startEmitting() {
        GlobalScope.launch(Dispatchers.Main) {
            while (true) {
                delay(1000)
                values = values + random.nextInt().toString()
                observable.updateValue(values)
            }
        }
    }
}

Y finalmente, lo único que habría que hacer desde la Activity es suscribirse en el onCreate() y desuscribirse en el onDestroy(), y crear el observer para escuchar los cambios:

private val observer = { items: List<String> -> 
        adapter.items = items 
}

override fun onCreate(savedInstanceState: Bundle?) {
    ...
    ItemsProvider.observable.subscribe(observer)
}

override fun onDestroy() {
    ItemsProvider.observable.unsubscribe(observer)
    super.onDestroy()
}

LiveData y la Programación Reactiva

Si te fijas, si ya usabas LiveData en tus proyectos, todo esto te sonará mucho. Este componente de la librería de Lifecycle de Android hace prácticamente lo mismo, y además es capaz de observar el ciclo de vida de la Activity, así que ni siquiera necesitamos desuscribirnos:

object ItemsProvider {
    private val _observable = MutableLiveData<List<String>>()
    val observable: LiveData<List<String>> get() = _observable
    ...
}

Y en la Activity:

ItemsProvider.observable.observe(this, Observer {
    adapter.items = it
})

Si quieres aprender más sobre LiveData, ya hablé sobre ellos en mi artículo sobre MVVM.

Conclusión

Como ves, los conceptos realmente son muy sencillos.

En Programación Reactiva, en lugar de ir nosotros activamente a buscar los datos, existen unos flujos de datos a los que nosotros reaccionamos cuando recibimos nueva información.

Aquí me he permitido algunas licencias con el objetivo de entender bien la bases, pero en los siguientes artículos nos iremos adentrando más a fondo en los conceptos.

Y sobre todo en cómo usar Flow: la librería basada en corrutinas que nos permite implementar la programación Reactiva en Kotlin y en Android.

Tienes el código subido en este repositorio de GitHub.

Y de nuevo, si no quieres perderte nada, y prefieres ver este contenido en vídeo, únete a mi canal de YouTube. ¡Nos vemos por allí!

Flows en Kotlin: Todo lo que necesitas saber

Flow es un componente de la librería de corrutinas que nos permite implementar la programación reactiva. Es el sustituto natural de RxJava, ya que la gran mayoría de las cosas que se pueden hacer...

Flows en Kotlin

Usando Flow en un proyecto Android

En el artículo anterior veíamos un ejemplo sobre Flow, pero eran los típicos ejemplos que en el día a día no nos llevan a ninguna parte. ¿Cómo encaja todo esto dentro de una aplicación Android?...

Flow en proyecto Android

Room con Flow, y un ejemplo de paginación

Esto avanza, y si en el artículo anterior veíamos cómo usar Flow en un proyecto Android, aquí vamos a ir más allá e integrarlo con Flow. En realidad la integración es extremadamente sencilla,...

Flow con Room

StateFlow, el sustituto natural de LiveData

Llevamos una serie de capítulos dedicados a Flow, donde hemos visto desde los conceptos básicos de Flow hasta cómo usar Flow en un ejemplo real. Incluso hemos visto cómo Flow se integra con Room....

StateFlow

Quizá también te interese…

Cómo modularizar una Aplicación Android

Cómo modularizar una Aplicación Android

Cómo modularizar una aplicación Android En este artículo, vamos a hablar sobre la modularización de aplicaciones Android. La modularización es un proceso que consiste en dividir una aplicación en varios módulos, para facilitar su mantenimiento y escalabilidad. La...

¿Qué es Kotlin Multiplataforma?

¿Qué es Kotlin Multiplataforma?

En el mundo actual, donde los dispositivos móviles están presentes en nuestra vida diaria, es fundamental para los desarrolladores crear aplicaciones que se adapten a diferentes sistemas operativos. Kotlin Multiplataforma es una herramienta que facilita la creación de...

5 trucos de Kotlin para escribir código más eficiente en Android

5 trucos de Kotlin para escribir código más eficiente en Android

El lenguaje de programación Kotlin se ha convertido en el más popular para el desarrollo de aplicaciones de Android en los últimos años. Su sintaxis concisa y moderna, junto con su capacidad para mejorar la eficiencia de código, lo convierten en una opción atractiva...

0 comentarios

Enviar un comentario

Los datos personales que proporciones a través de este formulario quedarán registrados en un fichero de DevExpert, S.L.U., con el fin de gestionar los comentarios que realizas en este blog. La legitimación se realiza a través del consentimiento de la parte interesada. Si no se acepta, no podrás comentar en este blog. Los datos que proporciona solo se utilizan para evitar el correo no deseado y no se usarán para nada más. Puede ejercer los derechos de acceso, rectificación, cancelación y oposición en contacto@devexperto.com.

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Acepto la política de privacidad *