Kotlin Coroutines vs RxJava: programacao assincrona em 2026

A programacao assincrona e essencial no desenvolvimento moderno, seja para chamadas de rede, operacoes de banco de dados ou processamento em background. Durante anos, RxJava foi a solucao 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)
Integracao AndroidOficial (Jetpack)Biblioteca terceira
Peso da dependenciaLeve (~1.5MB)Pesado (~3MB+)
Suporte multiplatformSimApenas JVM
CancellamentoEstruturado (scope)Manual (Disposable)

Modelo de programacao

A diferenca fundamental esta na forma como cada abordagem expressa operacoes assincronas.

Coroutines: codigo sequencial com suspensao

Coroutines permitem escrever codigo assincrono que parece sincrono. A palavra-chave suspend marca funcoes 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)
}

// Execucao 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 versao com Coroutines e significativamente mais legivel. A versao RxJava requer conhecimento de operadores como zip, flatMap e map para expressar a mesma logica.

Tratamento de erros

Coroutines: try/catch padrao

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, voce precisa conhecer onErrorReturn, onErrorResumeNext, retry e outros operadores especificos 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 praticos, Coroutines geralmente apresentam menor uso de memoria e latencia comparable ou inferior a RxJava para a maioria dos cenarios. A principal razao e que coroutines suspensas nao alocam objetos intermediarios na mesma proporcao que cadeias de operadores RxJava.

MetricaCoroutinesRxJava
Memoria por operacaoBaixa (state machine)Moderada (objetos Observable)
Overhead de criacaoMinimoModerado (cadeia de operadores)
Troca de contextoEficiente (Dispatchers)Schedulers (thread pool)
Cold startRapidoMais lento (setup da cadeia)

Para cenarios com milhares de operacoes concorrentes, coroutines escalam melhor porque cada coroutine ocupa poucos bytes na memoria, enquanto cada subscription RxJava mantem uma cadeia de objetos.

Quando usar cada um

Escolha Kotlin Coroutines quando:

  • Voce inicia um projeto novo em Kotlin
  • Precisa de integracao nativa com Jetpack (ViewModel, Room, Compose)
  • Quer codigo mais legivel e facil de depurar
  • Precisa de suporte Kotlin Multiplatform
  • A equipe tem experiencia limitada com programacao reativa
  • Cancelamento automatico e importante

Escolha RxJava quando:

  • O projeto existente ja usa RxJava extensivamente
  • Voce precisa de operadores complexos de combinacao de streams que nao tem equivalente direto em Flow
  • A equipe ja domina o modelo reativo
  • Voce trabalha com um codebase Java que nao pode migrar para Kotlin

Migracao gradual

Ambas as abordagens podem coexistir. A biblioteca kotlinx-coroutines-rx3 oferece funcoes 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 integracao com o ecossistema Android Jetpack e nativa, o modelo de cancelamento estruturado previne vazamentos, e o codigo resultante e mais legivel. RxJava permanece uma opcao valida para projetos existentes que ja investiram no modelo reativo, mas a tendencia clara do ecossistema e em direcao a Coroutines. Se voce esta comecando hoje, invista seu tempo em dominar Coroutines e Flow.