CoroutineScope — o que é, ciclo de vida e funcionamento em corrotinas

Autor: IT Sectr Publicado: 2026-06-22 Tempo de leitura: 10 min

CoroutineScope é uma interface Kotlin que define o ciclo de vida de uma corrotina e fornece um contexto para lançar novas corrotinas. De acordo com a documentação Kotlin, 2025, cada instância CoroutineScope contém um CoroutineContext e gerencia todas as corrotinas lançadas dentro dele. Quando o scope é cancelado, todas as corrotinas filhas são automaticamente canceladas, prevenindo vazamentos de memória.

Principais pontos

  • CoroutineScope — uma interface com um único campo CoroutineContext, definindo o ciclo de vida das corrotinas
  • Job — um elemento de contexto responsável pelo cancelamento: cancelar o scope cancela todas as corrotinas filhas
  • Concorrência estruturada — o princípio onde corrotinas filhas estão vinculadas ao scope pai
  • GlobalScope — um scope para toda a aplicação que não é recomendado devido ao risco de vazamentos de memória
  • supervisorScope — um scope especial onde cancelar uma corrotina filha não cancela as demais

O que é CoroutineScope em Kotlin?

CoroutineScope é uma interface fundamental da biblioteca kotlinx.coroutines que serve como contêiner para corrotinas. Ela define os limites do ciclo de vida das corrotinas: quando o scope é concluído, todas as corrotinas dentro dele são automaticamente canceladas.

kotlin
public interface CoroutineScope {
    public val coroutineContext: CoroutineContext
}

A interface contém apenas um campo — coroutineContext. Através dele, o scope fornece um dispatcher (Dispatcher), um job (Job), um manipulador de exceções e outros elementos de contexto para todas as corrotinas lançadas dentro dele.

Papel na biblioteca kotlinx.coroutines

Todas as funções de lançamento de corrotinas — launch, async, runBlocking — são funções de extensão em CoroutineScope. Isso significa que só podem ser chamadas quando um objeto scope está disponível. Este design garante que cada corrotina tenha um pai e ciclo de vida claramente definidos.

Onde CoroutineScope é usado

No Android, cada componente arquitetural tem seu próprio scope: viewModelScope para ViewModel, lifecycleScope para Activity/Fragment. Em aplicações servidoras, um scope pode ser vinculado a uma requisição HTTP ou a um pool de conexões de banco de dados.

Como CoroutineScope funciona: Job e concorrência estruturada

Entender o funcionamento interno de CoroutineScope requer familiaridade com o conceito de Job e o princípio de concorrência estruturada.

Job — a tarefa da corrotina

Cada corrotina ao ser lançada retorna um objeto Job (ou Deferred para async). Job representa uma tarefa com um ciclo de vida finito: New, Active, Completing, Completed, Cancelling, Cancelled. Os objetos Job formam uma estrutura de árvore:

  • Job pai — o scope no qual a corrotina foi lançada
  • Job filho — cada corrotina lançada via launch/async
  • Cancelamento do pai → cancelamento de todos os filhos
  • Exceção em um filho → cancelamento do pai (exceto em supervisorScope)

O princípio da concorrência estruturada

Concorrência estruturada é um princípio arquitetural chave do Kotlin Coroutines, onde o ciclo de vida da corrotina está vinculado ao ciclo de vida do seu scope. Isso contrasta com o modelo “disparar e esquecer”, onde uma corrotina continua viva após o scope ser concluído. Vantagens da concorrência estruturada:

  • Ciclo de vida previsível — quando o scope é concluído, todas as corrotinas são garantidamente interrompidas
  • Tratamento automático de erros — uma exceção em qualquer corrotina filha se propaga ao scope
  • Sem vazamentos de memória — nenhuma corrotina continua executando após o scope ser concluído
  • Hierarquia clara — o código reflete a estrutura lógica das operações paralelas

Ciclo de vida do CoroutineScope

Quando scope.cancel() é chamado, o Job do scope transiciona para o estado Cancelled, que recursivamente cancela todos os Jobs filhos. Após o cancelamento, o scope só pode ser reutilizado se uma nova instância de CoroutineScope for criada.

Criação e configuração de CoroutineScope

Você pode criar um CoroutineScope através de uma função fábrica ou implementando a interface em sua classe. Vamos ver ambas as abordagens.

Função fábrica CoroutineScope()

kotlin
val scope = CoroutineScope(Dispatchers.Default + SupervisorJob())

scope.launch {
    println("Running on ${Thread.currentThread().name}")
}

A função fábrica recebe um CoroutineContext e cria um scope com o contexto especificado. O exemplo usa Dispatchers.Default para tarefas intensivas de CPU e SupervisorJob, que isola exceções entre corrotinas filhas.

Implementando a interface via composição

kotlin
class MyRepository {
    private val scope = CoroutineScope(Dispatchers.IO + Job())

    suspend fun fetchData(): Data = scope.async {
        api.getData()
    }.await()

    fun cleanup() {
        scope.cancel()
    }
}

Armazenamos o scope como um campo da classe e chamamos manualmente cleanup para cancelá-lo. Esta abordagem é adequada para componentes com ciclo de vida gerenciado — por exemplo, repositórios ou gerentes.

Implementação via delegação

Kotlin permite delegar a implementação de CoroutineScope através da palavra-chave by:

kotlin
class DataLoader : CoroutineScope by CoroutineScope(Dispatchers.IO) {
    fun load() {
        launch {
            // coroutine runs in DataLoader scope
        }
    }
}

Esta abordagem é conveniente quando a própria classe é um scope e deseja fornecer métodos de lançamento de corrotinas. No entanto, cuidado: a classe herda todos os métodos de CoroutineScope, incluindo cancel, o que pode quebrar o encapsulamento.

GlobalScope vs CoroutineScope personalizado

GlobalScope é um singleton CoroutineScope para toda a aplicação. Seu uso em código de produção não é oficialmente recomendado.

Problemas com GlobalScope

  • Falta de concorrência estruturada — corrotinas em GlobalScope não estão vinculadas ao ciclo de vida do componente
  • Vazamentos de memória — uma corrotina pode continuar executando após o fechamento da Activity/Fragment
  • Testes difíceis — GlobalScope não pode ser substituído em testes
  • Consumo não controlado de recursos — muitas corrotinas podem executar por mais tempo que o esperado

Quando GlobalScope é justificado

JetBrains permite GlobalScope apenas em cenários raros: processos em segundo plano no nível da aplicação que devem viver mesmo após o fechamento de todas as Activities (por exemplo, sincronização de dados, análises). Mas mesmo nesses casos, é preferível criar seu próprio scope com CoroutineScope(SupervisorJob()).

Recomendação

Sempre use um CoroutineScope personalizado com gerenciamento explícito de ciclo de vida. No Android, estes são viewModelScope e lifecycleScope. Em aplicações servidoras, crie um scope para cada requisição ou pool de conexões.

coroutineScope vs supervisorScope: qual a diferença

Ambas as funções são funções suspend que criam um scope temporário para tarefas paralelas, mas seu comportamento com exceções difere fundamentalmente.

CaracterísticacoroutineScopesupervisorScope
Comportamento em erroUma exceção em uma corrotina filha cancela todas as outrasUma exceção em uma corrotina filha NÃO cancela as outras
Propagação de erroSim, a primeira exceção é propagada para foraSim, a primeira exceção é propagada para fora
Job padrãoJob() — filhos vinculados ao paiSupervisorJob() — filhos não dependem entre si
Caso de uso típicoOperação atômica de múltiplas etapasTarefas paralelas independentes (cargas de UI)

Quando escolher coroutineScope

Use coroutineScope quando múltiplas operações paralelas formam uma única operação atômica. Por exemplo, carregar dados de três servidores: se uma requisição falha, as demais não fazem sentido.

kotlin
suspend fun loadProductPage(): ProductPage = coroutineScope {
    val product = async { api.getProduct() }
    val reviews = async { api.getReviews() }
    ProductPage(product.await(), reviews.await())
}

Se getProduct ou getReviews lançar uma exceção — ambas as corrotinas são canceladas e a exceção é propagada para o código chamador.

Quando escolher supervisorScope

Use supervisorScope quando operações paralelas não dependem umas das outras. Por exemplo, carregar dados de perfil em várias seções independentes: se a seção de recomendações falhar, o cabeçalho do perfil e a lista de amigos devem ser exibidos.

Erros comuns ao trabalhar com CoroutineScope

Vamos ver os erros mais comuns de desenvolvedores ao usar CoroutineScope em Kotlin.

Erro 1: Esquecer de cancelar o scope

O cenário mais comum de vazamento de corrotina é criar um scope sem chamar cancel quando o componente termina. Se o scope não for cancelado, as corrotinas continuam executando, mantendo referências a objetos. No Android, use viewModelScope ou lifecycleScope, que são cancelados automaticamente.

Erro 2: Usar GlobalScope em Activity ou Fragment

GlobalScope ignora o ciclo de vida dos componentes Android. Uma corrotina lançada em GlobalScope após o fechamento de uma Activity continuará executando e tentará atualizar a UI — causando uma falha. Sempre use lifecycleScope para componentes de UI.

Erro 3: Reutilizar um scope cancelado

Após chamar cancel(), o scope não pode ser reutilizado — todas as corrotinas dentro dele já foram concluídas. Crie uma nova instância de CoroutineScope através da função fábrica. Job() não suporta reativação.

Erro 4: Delegação incorreta da interface CoroutineScope

Ao delegar com by, a classe ganha um método cancel() público que pode ser chamado de qualquer lugar, quebrando o encapsulamento. Armazene o scope como um campo privado em vez de delegar a interface.

Perguntas frequentes

Qual a diferença entre CoroutineScope e CoroutineContext?

CoroutineScope é uma interface que possui um CoroutineContext e é responsável pelo ciclo de vida das corrotinas. CoroutineContext é um conjunto de elementos (dispatcher, job, manipulador de erros) que define “como” uma corrotina executa. Uma diferença: o scope cria corrotinas, enquanto o contexto controla seu comportamento.

Pode-se criar um CoroutineScope com SupervisorJob?

Sim, é um padrão padrão: CoroutineScope(Dispatchers.IO + SupervisorJob()). SupervisorJob previne o cancelamento em cascata de corrotinas filhas quando uma delas lança uma exceção. Isso é útil para tarefas paralelas independentes onde um erro em uma não deve parar as outras.

Quantas corrotinas um CoroutineScope pode conter?

Não há limite no número de corrotinas em um scope — elas são limitadas apenas pela memória disponível e configurações do dispatcher. O limite prático é tipicamente de milhares de corrotinas ativas em um único scope. No entanto, um grande número de corrotinas pode indicar problemas arquiteturais.

Como testar código com CoroutineScope?

A forma correta é passar o scope para a classe através do construtor ou usar runBlockingTest / runTest de kotlinx-coroutines-test. Em testes, você pode substituir o scope por TestCoroutineDispatcher e controlar a execução das corrotinas manualmente.

Uma corrotina pode ter seu próprio scope?

Não, um scope é um contêiner externo para uma corrotina. A corrotina em si não é um scope. No entanto, dentro de uma corrotina você pode criar um novo scope através de coroutineScope ou supervisorScope para lançar corrotinas filhas em paralelo.

Resumo

  • CoroutineScope — uma interface com campo coroutineContext, definindo o ciclo de vida das corrotinas lançadas dentro dele
  • Concorrência estruturada — cancelar o scope cancela automaticamente todas as corrotinas filhas, prevenindo vazamentos de memória
  • Job e SupervisorJob — dois modos de tratamento de erros: cancelamento em cascata (Job) e erros isolados (SupervisorJob)
  • GlobalScope — não recomendado para produção por falta de vinculação ao ciclo de vida
  • coroutineScope vs supervisorScope — operações paralelas atômicas vs tarefas paralelas independentes
  • viewModelScope e lifecycleScope — scopes prontos para Android, cancelados automaticamente ao finalizar o componente
  • Função fábrica — a forma preferida de criar um scope via CoroutineContext + chamada explícita a cancel

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.

Discutir o projeto

Leia também