runBlocking — um Coroutine Builder em Kotlin que bloqueia a thread atual até que a corrotina passada seja concluída. Ao contrário de launch e async, não é uma função suspend e pode ser chamado de código normal (bloqueante). De acordo com a documentação JetBrains, 2024, runBlocking serve como uma ponte entre os mundos síncrono e assíncrono, permitindo iniciar corrotinas da função main e testes.
Principais conclusões
runBlocking é uma função Kotlin que cria um novo CoroutineScope e executa a corrotina passada, bloqueando a thread atual até sua conclusão completa. Ao contrário de todos os outros Coroutine Builders, runBlocking não é uma função suspend e pode ser chamado de código síncrono comum. A assinatura de runBlocking recebe um CoroutineContext e um bloco suspend, retornando um resultado do tipo T.
public fun <T> runBlocking(
context: CoroutineContext = EmptyCoroutineContext,
block: suspend CoroutineScope.() -> T
): T
runBlocking inicia um novo event-loop na thread atual. Quando a corrotina chama uma função suspend (por exemplo, delay() ou await()), runBlocking bloqueia a thread e executa outras corrotinas agendadas na mesma thread até que a suspensa seja retomada. Isso é bloqueio cooperativo — a thread não fica ociosa, mas processa outras corrotinas.
O mecanismo interno do runBlocking é baseado em um event-loop: ao chamar uma função suspend, o runBlocking pausa a execução do bloco atual e executa outras corrotinas da fila. Quando a função suspend é concluída, a execução é retomada. Este ciclo continua até que todas as corrotinas terminem.
runBlocking usa seu próprio pool de thread única para executar corrotinas. Ao contrário de Dispatchers.IO ou Default, runBlocking não alterna threads — ele manipula todas as corrotinas na thread atual, intercalando sua execução. Este é o único builder que garante a execução na mesma thread.
fun main() {
val threadName = Thread.currentThread().getName()
println("Antes de runBlocking em $threadName")
val result = runBlocking {
println("Dentro de runBlocking em ${Thread.currentThread().getName()}")
delay(500L)
"Done"
}
println("Depois de runBlocking: $result")
}
A saída mostrará que todas as três instruções println são executadas na mesma thread. runBlocking não alterna threads, mas organiza multitarefa cooperativa dentro de uma única thread usando um event-loop.
runBlocking é justificado em três cenários: o ponto de entrada main() em aplicações de console, testes unitários de funções suspend e ponte — chamar código suspend de bibliotecas baseadas em callbacks ou bloqueantes. Em código Android de produção, usá-lo na thread principal é estritamente proibido.
| Cenário | Aplicabilidade | Riscos |
|---|---|---|
| main() de aplicação de console | Sim | Nenhum — é o ponto de entrada, a thread não bloqueia a UI |
| Testes JUnit | Sim | Mínimos — testes são síncronos por definição |
| Thread UI do Android | Não | ANR, lentidão, congelamento da interface |
| Callback → Coroutine | Sim, com cautela | Bloqueio do pool de threads em operações longas |
Para testes Android, use kotlinx-coroutines-test com TestDispatcher em vez de runBlocking. Isso dá controle de tempo, limpeza automática e isolamento de testes.
Na maioria dos cenários, runBlocking pode e deve ser substituído por alternativas assíncronas. Para Android, são viewModelScope, lifecycleScope ou CoroutineScope com o dispatcher adequado. Para testes — TestCoroutineDispatcher e runTest.
// Ruim: runBlocking na thread principal do Android
runBlocking(Dispatchers.Main) {
val result = networkApi.fetchData()
textView.setText(result)
}
// Bom: lifecycleScope
lifecycleScope.launch {
val result = withContext(Dispatchers.IO) { networkApi.fetchData() }
textView.setText(result)
}
Para testes, a substituição é runTest de kotlinx-coroutines-test. Ele cria um TestCoroutineScope com tempo virtual, permitindo testar atrasos sem espera real. Isso acelera os testes e os torna determinísticos.
O cenário mais comum é testar funções suspend. runBlocking em testes permite aguardar sincronamente um resultado de corrotina sem alterar a arquitetura. O segundo cenário são bibliotecas com API de callbacks, onde funções suspend são chamadas de um contexto bloqueante via runBlocking.
// Testar função suspend com runBlocking
class RepositoryTest {
@Test
fun `fetchUser returns correct data`() {
val repository = UserRepository(FakeApi())
val result = runBlocking {
repository.fetchUser("123")
}
assertEquals("John", result.name)
assertEquals("john@test.com", result.email)
}
}
Para a ponte entre callbacks e o mundo suspend, use CompletableDeferred em combinação com runBlocking em vez de callbacks — isso simplifica cadeias de operações assíncronas e melhora a legibilidade do código.
O uso inadequado de runBlocking é um dos erros comuns ao migrar de uma abordagem bloqueante para corrotinas. Principais problemas: chamada na thread principal do Android, aninhamento de runBlocking, uso dentro de funções assíncronas e execução de operações longas com runBlocking.
Regra de ouro: runBlocking é uma ponte, não uma substituição. Use-o apenas para conectar mundos bloqueantes e não bloqueantes. Para todas as outras tarefas, use launch, async ou lifecycleScope.
Perguntas frequentes
runBlocking é o único builder que não é uma função suspend. Ele inicia um event-loop na thread atual e não retorna o controle até que todas as corrotinas sejam concluídas. launch e async retornam o controle imediatamente, executando a corrotina em segundo plano.
Não recomendado. ViewModel tem um viewModelScope embutido que gerencia automaticamente as corrotinas e as cancela quando destruído. runBlocking no ViewModel bloqueia a thread e não responde ao cancelamento do ciclo de vida.
Use runTest da biblioteca kotlinx-coroutines-test. Ele fornece um TestCoroutineScope com controle de tempo virtual, cancelamento automático e execução determinística.
Event-loop é um ciclo de processamento de eventos dentro do runBlocking. Quando uma corrotina é suspensa (ex. delay()), o event-loop alterna para executar outras corrotinas prontas na mesma thread. Isso cria a ilusão de multitarefa sem alternância de thread.
runBlocking aninhado na mesma thread cria um deadlock — o bloco externo aguarda o interno, mas o interno não pode começar até que o externo termine. Em threads diferentes é permitido, mas fortemente desaconselhado devido à complexidade de depuração.
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também