Las corutinas en Kotlin son hilos ligeros que soportan la suspensión y reanudación de cálculos sin bloquear los hilos del sistema operativo. Se inician a través de funciones como launch, async dentro de un CoroutineScope padre.
La despachación ocurre a través de objetos CoroutineDispatcher, que determinan en qué hilo/eje se ejecuta la corutina (Dispatchers.Main, Dispatchers.IO, Dispatchers.Default).
Ejemplo:
fun main() = runBlocking { launch(Dispatchers.IO) { val data = getDataFromNetwork() withContext(Dispatchers.Main) { updateUI(data) } } }
Cancelación: Las corutinas se cancelan mediante la cancelación de su job/Scope, lo que lanza una excepción interna CancellationException. Las corutinas deben verificar periódicamente la bandera de cancelación o llamar a funciones de suspensión para finalizar correctamente.
Manejo de errores: Las excepciones en las corutinas pueden "hundirse" — por ejemplo, si no manejas un error en una corutina hija, esta solo finalizará su trabajo, y el padre no se enterará del problema. Para esto existen mecanismos como SupervisorJob y CoroutineExceptionHandler.
Matices en la transmisión de contexto:
¿Qué ocurrirá si dentro de una corutina padre, una de las hijas finaliza con un error mientras que las otras continúan trabajando? ¿Serán canceladas todas las hijas?
Muchos creen erróneamente que el error "permanece en la hija".
Respuesta correcta: Si se utiliza un Job normal (o launch), todas las corutinas hijas se cancelan automáticamente al ocurrir un error en cualquiera de las hijas. Para que las corutinas hijas no se cancelen, se utiliza SupervisorJob o supervisorScope:
supervisorScope { launch { error("fail") } launch { println("Este código funcionará") } }
Historia
Manejo incorrecto de errores - caída del flujo: En el proyecto se utilizaron corutinas para cargar datos. Las excepciones "se hundieron" dentro de las corutinas hijas, lo que provocó que el nivel de reintento/recuperación no funcionara, y la pantalla principal permaneciera vacía sin ningún mensaje de error para el usuario.
Historia
Congelamiento de UI debido al uso de Dispatchers.Main: Un joven desarrollador de Android lanzó una tarea computacional pesada en Dispatchers.Main — la UI comenzó a "congelarse". No había comprensión de que cualquier cálculo pesado debe ejecutarse en Dispatchers.Default o Dispatchers.IO.
Historia
AbortError al cancelar el scope padre: Uno de los desarrolladores no tuvo en cuenta que al cancelar el scope padre, todas las corutinas hijas se finalizan con la cancelación, y una operación crucial (guardar datos) no se completó.