Sobes.tech
Middle

Como configurar e usar corutinas em projetos Kotlin?

sobes.tech IA

Resposta da IA

Para configurar as corrotinas, é necessário adicionar a dependência correspondente no arquivo build.gradle.

// build.gradle (módulo app)
dependencies {
    //... outras dependências
    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 (se necessário Dispatchers.Main)
}

O uso de corrotinas inclui:

  1. Escopo (Scope): Definir o escopo no qual as corrotinas serão executadas e gerenciar seu ciclo de vida.

    • GlobalScope: Escopo de toda a aplicação, usar com cuidado, pois pode causar vazamentos de memória.
    • CoroutineScope: Criado explicitamente, permite controlar o cancelamento das corrotinas.
    • Escopos predefinidos em frameworks (por exemplo, lifecycleScope em Android ViewModel).
    // Exemplo com CoroutineScope
    val myScope = CoroutineScope(Dispatchers.Default + SupervisorJob()) // Criar escopo com Dispatcher e Job
    
    fun performBackgroundTask() {
        myScope.launch { // Iniciar uma corrotina
            // Processo de cálculo longo
        }
    }
    
    fun cancelAllTasks() {
        myScope.cancel() // Cancelar todas as corrotinas no escopo
    }
    
  2. Construtores de corrotinas (Builders): Funções para lançar corrotinas.

    • launch: Inicia uma corrotina que não retorna resultado explícito (retorna um Job).
    • async: Inicia uma corrotina que retorna resultado na forma de Deferred.
    import kotlinx.coroutines.*
    
    suspend fun fetchData() = coroutineScope { // coroutineScope - construtor para concorrência estruturada
        val data1 = async { networkCall1() } // async retorna um Deferred
        val data2 = async { networkCall2() }
    
        // ... processamento de dados após recebê-los
        data1.await() // Obter resultado de Deferred, suspende a corrotina até que seja concluído
        data2.await()
    }
    
    suspend fun networkCall1(): String {
        delay(1000) // Simulação de requisição de rede
        return "Data 1"
    }
    
    suspend fun networkCall2(): String {
        delay(1500)
        return "Data 2"
    }
    
    fun startFetching() = runBlocking { // runBlocking - construtor para bloquear a thread atual (para main ou testes)
        launch { // launch retorna um Job
            println("Fetching data...")
            fetchData()
            println("Data fetched!")
        }
    }
    
  3. Despachantes (Dispatchers): Determinam em qual thread ou pool de threads a corrotina será executada.

    • Dispatchers.Default: Para tarefas intensivas em CPU.
    • Dispatchers.IO: Para operações de E/S bloqueantes (arquivos, rede).
    • Dispatchers.Main: Thread principal (apenas para Android e Swing/JavaFX).
    • Dispatchers.Unconfined: Executa a corrotina na thread que a chamou, ela se suspende e depois é retomada na thread que a retomou.
    import kotlinx.coroutines.*
    
    fun simpleTask() {
        GlobalScope.launch(Dispatchers.IO) { // Especificar o despachante
            // Executar operação de E/S
            println("Executando na thread: ${Thread.currentThread().name}")
        }
    }
    
    fun main() = runBlocking {
        launch(Dispatchers.Default) {
            println("Thread padrão: ${Thread.currentThread().name}")
        }
        launch(Dispatchers.Unconfined) {
             println("Thread não confinado 1: ${Thread.currentThread().name}")
             delay(100) // Suspensão
             println("Thread não confinado 2: ${Thread.currentThread().name}") // Recomeço em outro thread (possivelmente)
        }
        delay(200) // Esperar a conclusão das corrotinas
    }
    
  4. Funções de suspensão (Suspending functions): Funções marcadas com a palavra-chave suspend, que podem ser suspensas e retomadas sem bloquear o thread. Só podem ser chamadas de outras funções de suspensão ou blocos de corrotinas.

  5. Tratamento de cancelamento e exceções: As corrotinas suportam cancelamento estruturado e propagação de exceções.

    • Cancelamento:
      import kotlinx.coroutines.*
      
      fun cancellableTask() = GlobalScope.launch {
          try {
              repeat(1000) { i ->
                  println("Trabalhando $i ...")
                  delay(100) // função de suspensão que respeita o cancelamento
              }
          } catch (e: CancellationException) {
              println("Tarefa foi cancelada!")
          } finally {
              println("Limpando...")
          }
      }
      
      fun main() = runBlocking {
          val job = cancellableTask()
          delay(500) // Esperar um pouco
          job.cancelAndJoin() // Cancelar e aguardar a finalização
      }
      
    • Exceções: No caso de launch, uma exceção não capturada levará à queda do aplicativo (no Android, por padrão). No caso de async, a exceção será lançada ao chamar await(). Para controle mais granular, usam-se CoroutineExceptionHandler ou SupervisorJob.
    import kotlinx.coroutines.*
    
    fun main() = runBlocking {
        val handler = CoroutineExceptionHandler { _, exception ->
            println("Exceção capturada: $exception")
        }
    
        val job = GlobalScope.launch(handler) { // Aplicar o manipulador
            throw IllegalStateException("Algo deu errado")
        }
        job.join() // Aguardar a finalização
    }