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