Depois que voce aprende o basico de coroutines, as perguntas que ficam sao sobre escopos e tratamento de excecoes. Vou mostrar como lidar com esses dois na pratica.
Comece importando o essencial:
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0")
O escopo define onde a coroutine vive e quando ela morre. O erro mais comum e lancar uma coroutine sem escopo, e ela continuar rodando depois que a tela fechou ou o servico terminou.
Exemplo com GlobalScope (nao faca isso):
GlobalScope.launch {
delay(5000)
println("Ainda rodando...")
}
Essa coroutine viveria para sempre. Use escopos com ciclo de vida definido.
No Android, use o viewModelScope:
class MeuViewModel : ViewModel() {
fun carregarDados() {
viewModelScope.launch {
val dados = api.buscarDados()
// atualiza a UI
}
}
}
Quando o ViewModel for limpo, todas as coroutines dentro dele sao canceladas automaticamente.
Em aplicacoes Spring, crie seu proprio escopo com CoroutineScope:
@Service
class MeuServico {
private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
fun processar() {
scope.launch {
delay(1000)
println("processamento feito")
}
}
@PreDestroy
fun cleanup() {
scope.cancel()
}
}
O SupervisorJob e importante: ele faz com que a falha de uma coroutine filha nao cancele as outras. Veja a diferenca:
val job = SupervisorJob()
val scope = CoroutineScope(job + Dispatchers.IO)
scope.launch {
delay(100)
throw RuntimeException("falha na 1")
}
scope.launch {
delay(200)
println("essa ainda executa")
}
Com Job normal, a segunda coroutine seria cancelada quando a primeira falhasse. Com SupervisorJob, ela continua.
Para tratamento de excecoes, use try/catch dentro da coroutine:
scope.launch {
try {
val resultado = api.chamadaPerigosa()
println(resultado)
} catch (e: Exception) {
println("erro na chamada: ${e.message}")
}
}
Ou use um CoroutineExceptionHandler global:
val handler = CoroutineExceptionHandler { _, exception ->
println("nao tratado: ${exception.message}")
}
scope.launch(handler) {
throw RuntimeException("algo deu errado")
}
O handler captura excecoes que nao foram tratadas dentro da coroutine. Mas ele nao funciona com async:
scope.async {
throw RuntimeException("isso nao vai pro handler")
}
No async, a excecao so aparece quando voce chama await():
scope.launch {
val deferred = scope.async {
throw RuntimeException("erro no async")
"resultado"
}
try {
deferred.await()
} catch (e: Exception) {
println("capturado no await: ${e.message}")
}
}
Para escopos em camadas, use withContext para trocar de dispatcher:
scope.launch {
val dados = withContext(Dispatchers.IO) {
// operacao bloqueante aqui
api.buscarDoBanco()
}
// volta para o dispatcher principal
println(dados)
}
Escolha o dispatcher certo: Dispatchers.Main para UI, Dispatchers.IO para rede e banco, Dispatchers.Default para processamento pesado.
That's all for now.
Thanks for reading!
Top comments (0)