ProgramaciónIngeniero de Software en Kotlin

¿Cómo funcionan las corutinas en Kotlin? Describe el mecanismo de inicio, la despachación, la cancelación y el manejo de errores. Proporciona un ejemplo de uso y aclara los matices relacionados con la transmisión de contexto y excepciones.

Supere entrevistas con el asistente de IA Hintsage

Respuesta

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:

  • El contexto (por ejemplo, Job, Dispatcher, elementos personalizados) se hereda al crear nuevas corutinas.
  • No se pueden simplemente pasar parámetros entre scopes si están cerrados o cancelados.

Pregunta trampa

¿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á") } }

Ejemplos de errores reales debido a la falta de conocimiento sobre el tema


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ó.