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 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.
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.
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.
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.
Entender o funcionamento interno de CoroutineScope requer familiaridade com o conceito de Job e o princípio de concorrência estruturada.
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:
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:
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.
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.
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.
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.
Kotlin permite delegar a implementação de CoroutineScope através da palavra-chave by:
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 é um singleton CoroutineScope para toda a aplicação. Seu uso em código de produção não é oficialmente recomendado.
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()).
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.
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ística | coroutineScope | supervisorScope |
|---|---|---|
| Comportamento em erro | Uma exceção em uma corrotina filha cancela todas as outras | Uma exceção em uma corrotina filha NÃO cancela as outras |
| Propagação de erro | Sim, a primeira exceção é propagada para fora | Sim, a primeira exceção é propagada para fora |
| Job padrão | Job() — filhos vinculados ao pai | SupervisorJob() — filhos não dependem entre si |
| Caso de uso típico | Operação atômica de múltiplas etapas | Tarefas paralelas independentes (cargas de UI) |
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.
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.
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.
Vamos ver os erros mais comuns de desenvolvedores ao usar CoroutineScope em Kotlin.
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.
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.
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.
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
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.
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.
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.
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.
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
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