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

CaracteristicaKotlin CoroutinesRxJava
LinguagemKotlin nativoJava (com extensoes Kotlin)
ParadigmaSuspensao sequencialStreams reativos
Curva de aprendizadoModeradaAlta
BackpressureFlow (nativo)Flowable (explicito)
Integração AndroidOficial (Jetpack)Biblioteca terceira
Peso da dependênciaLeve (~1.5MB)Pesado (~3MB+)
Suporte multiplatformSimApenas JVM
CancellamentoEstruturado (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.

MetricaCoroutinesRxJava
Memória por operaçãoBaixa (state machine)Moderada (objetos Observable)
Overhead de criaçãoMinimoModerado (cadeia de operadores)
Troca de contextoEficiente (Dispatchers)Schedulers (thread pool)
Cold startRapidoMais 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.