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í!

Author: Antonio Leiva

Soy un apasionado de Kotlin. Hace ya más de dos años que estudio el lenguaje y su aplicación a Android para ayudarte a ti a aprenderlo de la forma más sencilla posible.