viewModelScope: o que é, vinculação ao ViewModel e funcionamento no Android

Autor: IT Sectr Publicado: 2026-06-23 Tempo de leitura: 9 min

viewModelScope é um CoroutineScope integrado da biblioteca androidx.lifecycle que está vinculado ao ciclo de vida do ViewModel e é cancelado automaticamente quando ele é limpo. Segundo Google Android Developers, 2025, o viewModelScope é o mecanismo padrão para lançar corrotinas na arquitetura MVVM, garantindo operações assíncronas seguras sem risco de vazamento de memória. O ViewModelScope usa Dispatchers.Main por padrão, e todas as operações de IO dentro dele devem ser executadas via withContext.

Pontos principais

  • viewModelScope — CoroutineScope do lifecycle-viewmodel-ktx, cancelado quando ViewModel.onCleared() é chamado
  • Dispatchers.Main — dispatcher padrão, portanto atualizações de UI dentro das corrotinas são seguras
  • onCleared — callback que ativa o cancelamento automático de todas as corrotinas ativas no viewModelScope
  • clear() vs onCleared() — clear() é chamado pelo framework antes de onCleared, garantindo o cancelamento do escopo
  • launch — a principal forma de iniciar corrotinas no viewModelScope para operações fire-and-forget

O que é viewModelScope no Android?

viewModelScope é uma propriedade de extensão na interface ViewModel, adicionada na biblioteca lifecycle-viewmodel-ktx (a partir da versão 2.1.0). Ela fornece um CoroutineScope pronto para uso vinculado ao ciclo de vida do ViewModel.

kotlin
// Internal structure (simplified)
val ViewModel.viewModelScope: CoroutineScope
    get() {
        val scope = this.getTag(JOB_KEY)
        if (scope != null) return scope
        return CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
            .also { setTag(JOB_KEY, it) }
    }

O escopo é criado de forma preguiçosa (lazy) no primeiro acesso e armazenado em cache via setTag. Ele usa SupervisorJob, o que significa que uma exceção em uma corrotina filha não cancela as outras. O dispatcher padrão é Dispatchers.Main.immediate, que executa código na thread principal sem despacho adicional se já estiver na Main.

Como o viewModelScope recebe notificação de limpeza

Quando o ViewModel sai do ciclo de vida (a Activity é finalizada ou o Fragment é removido), o sistema chama clear(), que aciona onCleared(). Neste callback, o viewModelScope cancela seu Job, que encerra recursivamente todas as corrotinas ativas. O mecanismo é implementado através da interface Closeable, onde o Job do escopo é registrado como um recurso para fechamento automático.

Como o viewModelScope funciona: vinculação ao ciclo de vida do ViewModel

O mecanismo de vinculação do viewModelScope ao ciclo de vida do ViewModel é baseado em taggeamento e no callback onCleared. Vamos analisar passo a passo.

Passo 1: Criação do escopo no primeiro acesso

Quando o ViewModel executa viewModelScope.launch { ... }, o getter verifica se já existe um escopo armazenado sob a tag JOB_KEY. Se não existir, uma nova instância de CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate) é criada. O escopo é armazenado dentro do ViewModel através de um mapa interno de tags.

Passo 2: Ciclo de vida das corrotinas

Todas as corrotinas lançadas via viewModelScope.launch ou viewModelScope.async tornam-se filhas do SupervisorJob do escopo. Elas executam na thread principal (a menos que um dispatcher diferente seja especificado via withContext). Enquanto o ViewModel estiver vivo, as corrotinas podem estar ativas, suspensas ou concluídas.

Passo 3: Cancelamento no onCleared

Quando o sistema destrói o ViewModel, ViewModel.clear() é chamado. Dentro de clear(), ocorre o seguinte:

  • onCleared() é chamado para lógica de limpeza personalizada
  • Todos os recursos Closeable registrados via addCloseable são fechados
  • O Job do viewModelScope transita para o estado Cancelled
  • Todas as corrotinas filhas são canceladas recursivamente
  • As referências ao escopo são liberadas para o coletor de lixo

Resistência à rotação

Quando a tela é girada, a Activity é recriada, mas o ViewModel sobrevive (graças ao ViewModelStoreOwner). Isso significa que o viewModelScope permanece ativo e as corrotinas continuam executando sem interrupção. Após a recriação da Activity, o mesmo ViewModel (e o mesmo escopo) é reutilizado — o carregamento de dados não recomeça do zero.

viewModelScope na arquitetura MVVM

MVVM (Model-View-ViewModel) é a arquitetura recomendada pelo Google para aplicativos Android. viewModelScope ocupa um lugar central nela como executor de operações assíncronas.

O papel do viewModelScope nas camadas da arquitetura

CamadaComponentePapel do viewModelScope
UIActivity / FragmentObserva StateFlow/LiveData do ViewModel
ViewModelViewModelLança corrotinas via viewModelScope, gerencia o estado da UI
RepositoryRepositoryExpõe funções suspend chamadas das corrotinas do viewModelScope
DataDAO / ApiExecuta as requisições reais (Room, Retrofit)

O ViewModel lança corrotinas via viewModelScope, dentro das quais chama funções suspend do Repository. O resultado é transformado em StateFlow, que é observado pela camada de UI. Este design garante clara separação de responsabilidades e testabilidade independente de cada camada.

Por que viewModelScope no ViewModel e não no Fragment

Se as corrotinas fossem lançadas de um Fragment, seriam canceladas ao girar a tela junto com a destruição do Fragment. O ViewModel sobrevive à rotação, portanto as corrotinas lançadas em seu escopo continuam em execução. Esta é a principal vantagem do viewModelScope sobre o lifecycleScope ao carregar dados.

Exemplos de uso do viewModelScope

Vamos analisar três cenários práticos de uso do viewModelScope em um aplicativo Android com Kotlin.

Exemplo 1: Carregamento de dados na criação do ViewModel

kotlin
class ProfileViewModel(
    private val repo: ProfileRepository
) : ViewModel() {

    private val _profile = MutableStateFlow<Profile?>(null)
    val profile: StateFlow<Profile?> = _profile

    init {
        loadProfile()
    }

    private fun loadProfile() {
        viewModelScope.launch {
            val result = repo.getProfile()
            _profile.value = result
        }
    }
}

No bloco init, o carregamento do perfil começa imediatamente. A corrotina executa na thread principal (por padrão). O repositório usa withContext(Dispatchers.IO) para a requisição de rede dentro de sua função suspend, então o ViewModel não precisa se preocupar com a troca de threads.

Exemplo 2: Tratamento de erros com sealed class

kotlin
sealed class UiState {
    object Loading : UiState()
    data class Success(val data: List<Item>) : UiState()
    data class Error(val message: String) : UiState()
}
kotlin
fun fetchItems() {
    _state.value = UiState.Loading
    viewModelScope.launch {
        try {
            val items = repo.getItems()
            _state.value = UiState.Success(items)
        } catch (e: Exception) {
            _state.value = UiState.Error(e.message ?: "Unknown error")
        }
    }
}

O estado da UI é descrito através de uma sealed class UiState. O ViewModel atualiza o estado a cada mudança. O Fragment se inscreve no StateFlow e reage apenas ao estado atual, ignorando chamadas obsoletas de rotações anteriores.

Exemplo 3: Cancelamento da corrotina anterior em uma nova requisição

kotlin
private var searchJob: Job? = null

fun search(query: String) {
    searchJob?.cancel()
    searchJob = viewModelScope.launch {
        delay(300)
        val results = repo.search(query)
        _searchResults.value = results
    }
}

A cada nova consulta de pesquisa, a corrotina anterior é cancelada. delay(300) implementa debounce — a pesquisa é executada apenas após 300 ms de inatividade. Isso reduz a carga do servidor e evita resultados obsoletos.

viewModelScope vs lifecycleScope: quando escolher cada um

Ambos os escopos são fornecidos pela biblioteca AndroidX Lifecycle, mas estão vinculados a ciclos de vida diferentes. A escolha depende do tipo de tarefa.

Comparação de escopos

CaracterísticaviewModelScopelifecycleScope
ProprietárioViewModelLifecycleOwner (Activity/Fragment)
Cancelado na rotaçãoNão (ViewModel sobrevive)Sim (Activity é recriada)
Dispatcher padrãoDispatchers.Main.immediateDispatchers.Main.immediate
Disponível emViewModelActivity, Fragment, Service
Caso de uso típicoCarregamento de dados, lógica de negóciosInterações de UI, animações

Recomendações do Google

O Google recomenda usar viewModelScope para todas as tarefas de carregamento e processamento de dados. O lifecycleScope deve ser usado para operações vinculadas a um momento específico do ciclo de vida da UI — por exemplo, iniciar uma animação na primeira aparição da tela ou se inscrever em atualizações de localização que devem parar ao sair da tela.

Erros comuns ao trabalhar com viewModelScope

Mesmo em uma API Android bem documentada, os desenvolvedores cometem erros típicos. Vamos analisar quatro dos problemas mais comuns.

Erro 1: Atualizar a UI após o cancelamento do escopo

O erro mais traiçoeiro é tentar atualizar StateFlow ou LiveData depois que o ViewModel foi limpo. Embora o viewModelScope seja cancelado no onCleared(), uma corrotina pode executar código antes que o cancelamento entre em vigor. Use isActive para verificar ou confie na conclusão do bloco catch.

Erro 2: Lançar corrotinas sem considerar o SupervisorJob

O viewModelScope usa SupervisorJob internamente, o que isola erros entre corrotinas. No entanto, se você lançar uma corrotina com seu próprio Job() dentro de viewModelScope.launch, essa corrotina se torna filha do SupervisorJob, mas não estará protegida contra cancelamento causado por erros em outras corrotinas.

Erro 3: Muitas corrotinas em um único escopo

Embora o viewModelScope não tenha um limite rígido, milhares de corrotinas ativas podem lentificar o sistema. Para listas longas de dados, use Flow com collectLatest em vez de criar corrotinas separadas para cada item.

Erro 4: Usar GlobalScope em vez de viewModelScope

Se GlobalScope for importado acidentalmente em vez de viewModelScope, a corrotina não será cancelada quando o ViewModel for limpo. Isso leva a vazamentos de memória e possíveis falhas. Sempre garanta que as corrotinas sejam lançadas via viewModelScope, especialmente em subclasses de Fragment.

Perguntas frequentes

Posso alterar o dispatcher padrão do viewModelScope?

Você não pode alterar diretamente o dispatcher do viewModelScope — ele está fixado como Dispatchers.Main.immediate. No entanto, dentro de uma corrotina você pode mudar para outro dispatcher via withContext. Para alterar o dispatcher em testes, use TestDispatcher através de uma Rule.

Como passar viewModelScope para o Repository?

Não passe o escopo para o Repository — isso quebra os princípios arquiteturais. O Repository deve expor funções suspend, e o próprio ViewModel gerencia as corrotinas através do viewModelScope. Se o Repository precisar de um escopo, repense a arquitetura em favor da Clean Architecture.

Por que o viewModelScope usa SupervisorJob?

O SupervisorJob garante que uma exceção em uma corrotina (por exemplo, um erro de carregamento em uma de várias requisições independentes) não cancele as outras corrotinas. Isso corresponde ao cenário do ViewModel, onde diferentes telas carregam dados independentes.

O viewModelScope está disponível no Jetpack Compose?

Sim, o viewModelScope está disponível em qualquer ViewModel independentemente do tipo de UI (View System ou Jetpack Compose). No Compose, as corrotinas também são lançadas via viewModelScope, enquanto para efeitos de UI são usados LaunchedEffect e rememberCoroutineScope.

O que acontece com uma corrotina quando viewModelScope.cancel() é chamado?

Chamar viewModelScope.cancel() cancela o escopo imediatamente — todas as corrotinas ativas terminam com CancellationException. Se viewModelScope.launch for chamado depois, um novo escopo é criado automaticamente no próximo acesso ao getter.

Resumo

  • viewModelScope — CoroutineScope vinculado ao ciclo de vida do ViewModel, cancelado automaticamente no onCleared()
  • SupervisorJob + Dispatchers.Main — configuração interna que garante isolamento de erros e acesso seguro à UI
  • Rotação de tela — o ViewModel sobrevive, portanto as corrotinas no viewModelScope continuam sem reinicialização
  • Arquitetura MVVM — viewModelScope é o elemento central para operações assíncronas na camada ViewModel
  • lifecycleScope — alternativa para operações vinculadas ao ciclo de vida de Activity/Fragment, não ao ViewModel
  • StateFlow — forma preferida de passar dados das corrotinas do viewModelScope para a UI via sealed class
  • GlobalScope é perigoso — substituir viewModelScope por GlobalScope leva a vazamentos de memória e falhas do aplicativo

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