¿Cómo configurar y usar corrutinas en proyectos de Kotlin?
sobes.tech AI
Respuesta de la IA
Para configurar las corrutinas, es necesario agregar la dependencia correspondiente en el archivo build.gradle.
// build.gradle (módulo app)
dependencies {
//... otras dependencias
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3") // Para JVM, Android, JS, Native
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3") // Para Android (si se necesita Dispatchers.Main)
}
El uso de corrutinas incluye:
-
Ámbito de vida (Scope): Definir el ámbito en el que las corrutinas se ejecutarán y gestionar su ciclo de vida.
GlobalScope: Ámbito de toda la aplicación, usar con precaución, ya que puede causar fugas de memoria.CoroutineScope: Se crea explícitamente, permite controlar la cancelación de las corrutinas.- Ámbitos predefinidos en frameworks (por ejemplo,
lifecycleScopeen Android ViewModel).
// Ejemplo con CoroutineScope val myScope = CoroutineScope(Dispatchers.Default + SupervisorJob()) // Crear Scope con Dispatcher y Job fun performBackgroundTask() { myScope.launch { // Iniciar corrutina // Proceso de cálculo largo } } fun cancelAllTasks() { myScope.cancel() // Cancelar todas las corrutinas en el Scope } -
Constructores de corrutinas (Builders): Funciones para lanzar corrutinas.
launch: Inicia una corrutina que no devuelve resultado explícito (devuelveJob).async: Inicia una corrutina que devuelve resultado en forma deDeferred.
import kotlinx.coroutines.* suspend fun fetchData() = coroutineScope { // coroutineScope - constructor para concurrencia estructurada val data1 = async { networkCall1() } // async devuelve Deferred val data2 = async { networkCall2() } // ... procesamiento de datos después de obtenerlos data1.await() // Obtener resultado de Deferred, suspende la corrutina hasta completar data2.await() } suspend fun networkCall1(): String { delay(1000) // Simulación de solicitud de red return "Data 1" } suspend fun networkCall2(): String { delay(1500) return "Data 2" } fun startFetching() = runBlocking { // runBlocking - constructor para bloquear el hilo actual (para main o pruebas) launch { // launch devuelve Job println("Fetching data...") fetchData() println("Data fetched!") } } -
Despachadores (Dispatchers): Determinan en qué hilo o grupo de hilos se ejecutará la corrutina.
Dispatchers.Default: Para tareas intensivas en CPU.Dispatchers.IO: Para operaciones de E/S bloqueantes (archivos, red).Dispatchers.Main: Hilo principal (solo para Android y Swing/JavaFX).Dispatchers.Unconfined: Ejecuta la corrutina en el hilo que la llamó, se suspende y luego se reanuda en el hilo que la reanudó.
import kotlinx.coroutines.* fun simpleTask() { GlobalScope.launch(Dispatchers.IO) { // Especificar despachador // Ejecutar operación de E/S println("Ejecutándose en el hilo: ${Thread.currentThread().name}") } } fun main() = runBlocking { launch(Dispatchers.Default) { println("Hilo por defecto: ${Thread.currentThread().name}") } launch(Dispatchers.Unconfined) { println("Hilo no confinado 1: ${Thread.currentThread().name}") delay(100) // Suspensión println("Hilo no confinado 2: ${Thread.currentThread().name}") // Reanudación en otro hilo (posiblemente) } delay(200) // Esperar a que terminen las corrutinas } -
Funciones de suspensión (Suspending functions): Funciones marcadas con la palabra clave
suspend, que pueden suspenderse y reanudarse sin bloquear el hilo. Solo pueden llamarse desde otras funciones de suspensión o bloques de corrutinas. -
Manejo de cancelación y excepciones: Las corrutinas soportan cancelación estructurada y propagación de excepciones.
- Cancelación:
import kotlinx.coroutines.* fun cancellableTask() = GlobalScope.launch { try { repeat(1000) { i -> println("Trabajando $i ...") delay(100) // función de suspensión que respeta la cancelación } } catch (e: CancellationException) { println("¡Tarea cancelada!") } finally { println("Limpieza...") } } fun main() = runBlocking { val job = cancellableTask() delay(500) // Esperar un poco job.cancelAndJoin() // Cancelar y esperar a que termine } - Excepciones: En
launch, una excepción no capturada causará la caída de la aplicación (en Android, por defecto). Enasync, la excepción se lanzará al llamar aawait(). Para control más granular, se usanCoroutineExceptionHandleroSupervisorJob.
import kotlinx.coroutines.* fun main() = runBlocking { val handler = CoroutineExceptionHandler { _, exception -> println("Excepción capturada: $exception") } val job = GlobalScope.launch(handler) { // Aplicar manejador throw IllegalStateException("Algo salió mal") } job.join() // Esperar a que termine } - Cancelación: