Programación reactiva en Kotlin: la biblioteca kotlinx.coroutines y Flow

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Programación reactiva en Kotlin: el poder de kotlinx.coroutines y Flow



La programación reactiva es un paradigma orientado al procesamiento asíncrono de flujos de datos y la propagación de cambios. En el ecosistema de Kotlin, la herramienta principal para implementar patrones reactivos es la biblioteca kotlinx.coroutines, y más concretamente su componente Flow. A diferencia de RxJava, Flow está estrechamente integrado con las corrutinas, lo que hace que el código sea más conciso y seguro.



Conceptos fundamentales de Flow



Flow es un flujo de datos asíncrono frío que emite valores secuencialmente. Soporta:

  • Flujos fríos — los datos comienzan a generarse solo cuando hay un suscriptor (operador terminal).
  • Contexto de ejecución — el flujo puede cambiar entre diferentes despachadores de corrutinas.
  • Cancelación mediante corrutinas — la cancelación de la corrutina padre finaliza automáticamente el Flow.


Instalación y configuración

Para usar Flow, agregue la dependencia en build.gradle.kts:

dependencies {    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1")}


Creación y consumo de Flow

Creemos un flujo simple de números con retardo:

import kotlinx.coroutines.*import kotlinx.coroutines.flow.*

fun simpleFlow(): Flow<Int> = flow { for (i in 1..5) { delay(100) // simulación de trabajo asíncrono emit(i) // emitimos el valor }}

fun main() = runBlocking { simpleFlow().collect { value -> println("Recibido: $value") }}

En este ejemplo, flow { ... } crea un flujo frío, y collect es el operador terminal que inicia la ejecución.



Transformación de datos con operadores

Flow proporciona un rico conjunto de operadores, similar a RxJava:

  • map — transforma cada valor.
  • filter — filtra valores según una condición.
  • flatMapConcat — combina flujos secuencialmente.
  • catch — maneja excepciones dentro del flujo.

Ejemplo con operadores:

fun transformFlow(): Flow<String> = flow {    emit("Kotlin")    emit("Java")    emit("Python")}.map { it.lowercase() } .filter { it.startsWith("k") }

fun main() = runBlocking { transformFlow().collect { println(it) } // Salida: kotlin}


Manejo de errores y finalización

Flow permite manejar excepciones de forma elegante con catch y ejecutar acciones finales mediante onCompletion:

fun errorProneFlow(): Flow<Int> = flow {    emit(1)    emit(2)    throw RuntimeException("¡Error!")    emit(3) // no se ejecutará}

fun main() = runBlocking { errorProneFlow() .catch { e -> println("Error capturado: ${e.message}") } .onCompletion { println("Flujo finalizado") } .collect { println(it) }}// Salida: 1, 2, Error capturado: ¡Error!, Flujo finalizado


Flujos calientes: StateFlow y SharedFlow

A diferencia del Flow frío, StateFlow y SharedFlow son flujos calientes que almacenan estado y pueden tener múltiples suscriptores.



StateFlow

Se utiliza para almacenar y observar el estado (por ejemplo, en ViewModel):

class CounterViewModel {    private val _counter = MutableStateFlow(0)    val counter: StateFlow<Int> = _counter.asStateFlow()

fun increment() { _counter.value++ }}

// En Activity o FragmentlifecycleScope.launch { viewModel.counter.collect { value -> textView.text = "Contador: $value" }}


SharedFlow

Adecuado para

Recommendations