Coroutines em Kotlin

Tempo: 20 min Nível: Intermediário

Por que usar Coroutines?

Vantagens
  • Operações assíncronas com código sequencial
  • Gerenciamento automático de threads
  • Cancelamento estruturado
  • Integração com ViewModel e Lifecycle
Comparação
Técnica Complexidade
Threads Alta
Callbacks Média
RxJava Alta
Coroutines Baixa

Configuração Inicial

Adicione estas dependências no build.gradle (Module)
build.gradle
dependencies {
    // Coroutines
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4'
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4'
    
    // Lifecycle Scope
    implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1'
    implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.1'
}

Conceitos Fundamentais

Suspensão

Funções que podem pausar sua execução sem bloquear threads

suspend fun fetchData() {
    // ...
}
Escopos

Definem o ciclo de vida das coroutines

viewModelScope.launch {
    // ...
}
Dispatchers

Controlam em qual thread a coroutine roda

withContext(Dispatchers.IO) {
    // ...
}

Exemplo Completo

Implementação em ViewModel com tratamento de erros:

UserViewModel.kt
class UserViewModel : ViewModel() {
    private val repository = UserRepository()
    
    private val _users = MutableLiveData<List<User>>()
    val users: LiveData<List<User>> = _users
    
    private val _error = MutableLiveData<String>()
    val error: LiveData<String> = _error
    
    fun loadUsers() {
        viewModelScope.launch {
            try {
                // IO thread para operações de rede
                val userList = withContext(Dispatchers.IO) {
                    repository.fetchUsers()
                }
                // Main thread para atualizar UI
                _users.value = userList
            } catch (e: Exception) {
                _error.value = "Falha ao carregar: ${e.message}"
            }
        }
    }
}
Atenção: Sempre use Dispatchers.IO para operações de rede/arquivo e Dispatchers.Main para atualizar UI.

Padrões Avançados

suspend fun fetchMultipleData() = coroutineScope {
    val deferred1 = async { repo.getData1() }
    val deferred2 = async { repo.getData2() }
    
    val result1 = deferred1.await()
    val result2 = deferred2.await()
    
    // Combina resultados
    result1 to result2
}

try {
    val result = withTimeout(5000) { // 5 segundos
        fetchDataFromNetwork()
    }
} catch (e: TimeoutCancellationException) {
    // Tratar timeout
}

suspend fun fetchWithRetry() {
    var currentDelay = 1000L // 1 segundo
    val maxDelay = 16000L    // 16 segundos
    
    repeat(5) { attempt ->
        try {
            return fetchData()
        } catch (e: Exception) {
            if (attempt == 4) throw e
            delay(currentDelay)
            currentDelay = (currentDelay * 2).coerceAtMost(maxDelay)
        }
    }
}

Fluxos Assíncronos (Flow)

Para streams de dados contínuos:

UserRepository.kt
class UserRepository {
    fun observeUsers(): Flow<List<User>> = flow {
        while (true) {
            val users = api.fetchUsers()
            emit(users)
            delay(60000) // Atualiza a cada minuto
        }
    }
}

// Na ViewModel
fun observeUsers() {
    viewModelScope.launch {
        repository.observeUsers()
            .catch { e -> _error.value = e.message }
            .collect { users -> _users.value = it }
    }
}
Dica: Combine Flow com Room Database para atualizações automáticas na UI quando os dados mudarem.

Materiais de Apoio

Projeto Exemplo

Aplicativo completo com os exemplos

Baixar
Vídeo Tutorial

Explicação em vídeo dos conceitos

Assistir
Cheat Sheet

Guia rápido de referência

Baixar
Conclusão
O que aprendemos:
  • Como configurar Coroutines em projetos Android
  • Padrões para operações assíncronas
  • Gerenciamento de threads com Dispatchers
  • Fluxos de dados com Flow

Próximos passos: