Kotlin Coroutines vs RxJava: programação assíncrona em 2026
A programação assíncrona e essencial no desenvolvimento moderno, seja para chamadas de rede, operações de banco de dados ou processamento em background. Durante anos, RxJava foi a solução dominante no ecossistema Android e JVM. Com a maturidade de Kotlin Coroutines e Flow, muitas equipes migraram ou consideram migrar. Este artigo compara as duas abordagens em profundidade.
Visao geral
| Caracteristica | Kotlin Coroutines | RxJava |
|---|---|---|
| Linguagem | Kotlin nativo | Java (com extensoes Kotlin) |
| Paradigma | Suspensao sequencial | Streams reativos |
| Curva de aprendizado | Moderada | Alta |
| Backpressure | Flow (nativo) | Flowable (explicito) |
| Integração Android | Oficial (Jetpack) | Biblioteca terceira |
| Peso da dependência | Leve (~1.5MB) | Pesado (~3MB+) |
| Suporte multiplatform | Sim | Apenas JVM |
| Cancellamento | Estruturado (scope) | Manual (Disposable) |
Modelo de programação
A diferenca fundamental esta na forma como cada abordagem expressa operações assíncronas.
Coroutines: código sequencial com suspensao
Coroutines permitem escrever código assíncrono que parece sincrono. A palavra-chave suspend marca funções que podem pausar sem bloquear a thread:
// Coroutines: leitura natural, de cima para baixo
suspend fun buscarPerfilCompleto(userId: Int): PerfilCompleto {
val usuario = api.buscarUsuario(userId) // suspende
val posts = api.buscarPosts(userId) // suspende
val seguidores = api.buscarSeguidores(userId) // suspende
return PerfilCompleto(usuario, posts, seguidores)
}
// Execução paralela quando necessario
suspend fun buscarPerfilParalelo(userId: Int): PerfilCompleto = coroutineScope {
val usuario = async { api.buscarUsuario(userId) }
val posts = async { api.buscarPosts(userId) }
val seguidores = async { api.buscarSeguidores(userId) }
PerfilCompleto(usuario.await(), posts.await(), seguidores.await())
}
RxJava: cadeias de operadores reativos
RxJava modela tudo como streams de dados transformados por operadores:
// RxJava: cadeia de operadores
fun buscarPerfilCompleto(userId: Int): Single<PerfilCompleto> {
return Single.zip(
api.buscarUsuario(userId),
api.buscarPosts(userId),
api.buscarSeguidores(userId)
) { usuario, posts, seguidores ->
PerfilCompleto(usuario, posts, seguidores)
}
}
// Sequencial em RxJava
fun buscarPerfilSequencial(userId: Int): Single<PerfilCompleto> {
return api.buscarUsuario(userId)
.flatMap { usuario ->
api.buscarPosts(userId).map { posts -> usuario to posts }
}
.flatMap { (usuario, posts) ->
api.buscarSeguidores(userId).map { seguidores ->
PerfilCompleto(usuario, posts, seguidores)
}
}
}
A versão com Coroutines e significativamente mais legivel. A versão RxJava requer conhecimento de operadores como zip, flatMap e map para expressar a mesma lógica.
Tratamento de erros
Coroutines: try/catch padrão
suspend fun carregarDados(): Resultado {
return try {
val dados = api.buscar()
Resultado.Sucesso(dados)
} catch (e: HttpException) {
Resultado.Erro("Erro HTTP: ${e.code()}")
} catch (e: IOException) {
Resultado.Erro("Sem conexao")
}
}
RxJava: operadores de erro
fun carregarDados(): Single<Resultado> {
return api.buscar()
.map<Resultado> { dados -> Resultado.Sucesso(dados) }
.onErrorReturn { e ->
when (e) {
is HttpException -> Resultado.Erro("Erro HTTP: ${e.code()}")
is IOException -> Resultado.Erro("Sem conexao")
else -> Resultado.Erro("Erro desconhecido")
}
}
}
O try/catch de Coroutines e familiar para qualquer desenvolvedor. Em RxJava, você precisa conhecer onErrorReturn, onErrorResumeNext, retry e outros operadores específicos para tratamento de erros.
Cancelamento
Coroutines: cancelamento estruturado
class MinhaViewModel : ViewModel() {
// viewModelScope cancela automaticamente quando ViewModel e destruido
fun carregarDados() {
viewModelScope.launch {
val dados = repositorio.buscar() // cancelado automaticamente
_estado.value = dados
}
}
}
// Cancelamento manual
val job = scope.launch {
while (isActive) {
val dado = canal.receive()
processar(dado)
}
}
job.cancel() // cancela limpo
RxJava: Disposable manual
class MinhaViewModel : ViewModel() {
private val disposables = CompositeDisposable()
fun carregarDados() {
disposables.add(
repositorio.buscar()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ dados -> _estado.value = dados },
{ erro -> _estado.value = Estado.Erro(erro) }
)
)
}
override fun onCleared() {
disposables.clear() // esqueceu? vazamento de memoria
}
}
O cancelamento estruturado de Coroutines e uma vantagem significativa. Com viewModelScope, lifecycleScope e coroutineScope, o cancelamento e automatico e hierarquico. Em RxJava, esquecer de chamar dispose() e uma fonte comum de bugs e vazamentos.
Fluxos de dados reativos
Kotlin Flow vs Observable/Flowable
// Kotlin Flow
fun observarUsuarios(): Flow<List<Usuario>> = flow {
while (true) {
val usuarios = api.buscarUsuarios()
emit(usuarios)
delay(30_000) // atualiza a cada 30 segundos
}
}.catch { e ->
emit(emptyList())
}.flowOn(Dispatchers.IO)
// Consumo
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.usuarios.collect { lista ->
adapter.submitList(lista)
}
}
}
// RxJava Observable
fun observarUsuarios(): Observable<List<Usuario>> {
return Observable.interval(0, 30, TimeUnit.SECONDS)
.flatMapSingle { api.buscarUsuarios() }
.onErrorReturnItem(emptyList())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
}
// Consumo
disposables.add(
viewModel.observarUsuarios()
.subscribe { lista -> adapter.submitList(lista) }
)
StateFlow vs BehaviorSubject
// Kotlin StateFlow
class ContadorViewModel : ViewModel() {
private val _contador = MutableStateFlow(0)
val contador: StateFlow<Int> = _contador.asStateFlow()
fun incrementar() { _contador.value++ }
}
// RxJava BehaviorSubject
class ContadorViewModel : ViewModel() {
private val _contador = BehaviorSubject.createDefault(0)
val contador: Observable<Int> = _contador.hide()
fun incrementar() { _contador.onNext(_contador.value!! + 1) }
}
Performance
Em benchmarks práticos, Coroutines geralmente apresentam menor uso de memória e latencia comparable ou inferior a RxJava para a maioria dos cenários. A principal razao e que coroutines suspensas não alocam objetos intermediarios na mesma proporcao que cadeias de operadores RxJava.
| Metrica | Coroutines | RxJava |
|---|---|---|
| Memória por operação | Baixa (state machine) | Moderada (objetos Observable) |
| Overhead de criação | Minimo | Moderado (cadeia de operadores) |
| Troca de contexto | Eficiente (Dispatchers) | Schedulers (thread pool) |
| Cold start | Rapido | Mais lento (setup da cadeia) |
Para cenários com milhares de operações concorrentes, coroutines escalam melhor porque cada coroutine ocupa poucos bytes na memória, enquanto cada subscription RxJava mantém uma cadeia de objetos.
Quando usar cada um
Escolha Kotlin Coroutines quando:
- Você inicia um projeto novo em Kotlin
- Precisa de integração nativa com Jetpack (ViewModel, Room, Compose)
- Quer código mais legivel é fácil de depurar
- Precisa de suporte Kotlin Multiplatform
- A equipe tem experiência limitada com programação reativa
- Cancelamento automatico e importante
Escolha RxJava quando:
- O projeto existente já usa RxJava extensivamente
- Você precisa de operadores complexos de combinacao de streams que não tem equivalente direto em Flow
- A equipe já domina o modelo reativo
- Você trabalha com um codebase Java que não pode migrar para Kotlin
Migração gradual
Ambas as abordagens podem coexistir. A biblioteca kotlinx-coroutines-rx3 oferece funções de conversao:
// RxJava para Flow
val flow: Flow<String> = observable.asFlow()
// Flow para Observable
val observable: Observable<String> = flow.asObservable()
// Single para suspend
val resultado: String = single.await()
Veredito
Em 2026, Kotlin Coroutines com Flow e a escolha recomendada para novos projetos Kotlin. A integração com o ecossistema Android Jetpack e nativa, o modelo de cancelamento estruturado previne vazamentos, e o código resultante e mais legivel. RxJava permanece uma opção válida para projetos existentes que já investiram no modelo reativo, mas a tendencia clara do ecossistema e em direcao a Coroutines. Se você esta comecando hoje, invista seu tempo em dominar Coroutines e Flow.