viewModelScope: vad det är, koppling till ViewModel och funktion i Android

Författare: IT Sectr Publicerad: 2026-06-23 Lästid: 9 min

viewModelScope — är en inbyggd CoroutineScope från androidx.lifecycle-biblioteket, som är kopplat till ViewModels livscykel och automatiskt avbryts vid dess rensning. Enligt Google Android Developers, 2025 är viewModelScope den standardmekanism för att starta koroutiner i MVVM-arkitektur som ger säker asynkron hantering utan risk för minnesläckor. ViewModelScope använder som standard Dispatchers.Main och alla IO-operationer inuti måste utföras via withContext.

Huvudpunkter

  • viewModelScope — CoroutineScope från lifecycle-viewmodel-ktx, avbryts vid ViewModels onCleared()
  • Dispatchers.Main — standarddispatcher, så UI-uppdateringar inuti koroutiner är säkra
  • onCleared — callback, vid anrop av vilken viewModelScope automatiskt avbryter alla aktiva koroutiner
  • clear() vs onCleared() — clear() anropas av ramverket före onCleared, vilket garanterar att scope avbryts
  • launch — det primära sättet att starta koroutiner i viewModelScope för fire-and-forget-operationer

Vad är viewModelScope i Android?

viewModelScope — är en extension-egenskap på ViewModel-gränssnittet, tillagd i lifecycle-viewmodel-ktx-biblioteket (från version 2.1.0). Det ger en färdig CoroutineScope kopplad till ViewModels livscykel.

kotlin
// Intern struktur (förenklad)
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) }
    }

Scope skapas lat (lazy) vid första åtkomst och cachas via setTag. SupervisorJob används, vilket innebär att ett undantag i en underordnad koroutin inte avbryter de andra. Standarddispatcher är Dispatchers.Main.immediate, som exekverar kod på huvudtråden utan ytterligare dispatchering om anropet redan är på Main.

Hur får viewModelScope rensningsmeddelande

När ViewModel lämnar livscykeln (Activity är avslutad eller Fragment är borttaget) anropar systemet clear(), som utlöser onCleared(). I denna callback avbryter viewModelScope sin Job, vilket rekursivt avslutar alla aktiva koroutiner. Mekanismen är implementerad via Closeable-gränssnittet, där scopes Job registreras som en resurs för automatisk stängning.

Hur fungerar viewModelScope: koppling till ViewModels livscykel

Mekanismen för att koppla viewModelScope till ViewModels livscykel baseras på taggning och callbacken onCleared. Låt oss steg för steg titta på hur det fungerar.

Steg 1: Skapa scope vid första åtkomst

När ViewModel utför viewModelScope.launch { ... } kontrollerar gettern om det finns ett sparat scope under taggen JOB_KEY. Om scope inte har skapats än — skapas en ny instans av CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate). Scope lagras inuti ViewModel via en intern taggkarta.

Steg 2: Koroutiners livscykel

Alla koroutiner som startas via viewModelScope.launch eller viewModelScope.async blir underordnade scopes SupervisorJob. De arbetar på huvudtråden (om ingen annan dispatcher anges via withContext). Så länge ViewModel lever — kan koroutiner vara aktiva, suspenderade eller slutförda.

Steg 3: Avbrytning vid onCleared

När systemet förstör ViewModel anropas ViewModel.clear(). Inuti clear() händer följande:

  • Anrop av onCleared() för användarlogik
  • Stängning av alla Closeable-resurser registrerade via addCloseable
  • viewModelScopes Job övergår till status Cancelled
  • Alla underordnade koroutiner avbryts rekursivt
  • Frigöring av referenser till scope för sophämtaren

Motståndskraft mot skärmrotation

Vid skärmrotation återskapas Activity, men ViewModel behålls (tack vare ViewModelStoreOwner). Detta innebär att viewModelScope förblir aktivt och koroutiner fortsätter utan avbrott. Efter återskapning av Activity används samma ViewModel (och samma scope) igen — dataladdning startar inte om.

viewModelScope i MVVM-arkitektur

MVVM (Model-View-ViewModel) — är den av Google rekommenderade arkitekturen för Android-applikationer. viewModelScope intar en central plats i den som utförare av asynkrona operationer.

viewModelScopes roll i arkitekturlagren

LagerKomponentviewModelScopes roll
UIActivity / FragmentObserverar StateFlow/LiveData från ViewModel
ViewModelViewModelStartar koroutiner via viewModelScope, hanterar UI-status
RepositoryRepositoryTar emot suspend-funktioner anropade från viewModelScopes koroutiner
DataDAO / ApiUtför faktiska förfrågningar (Room, Retrofit)

ViewModel startar via viewModelScope koroutiner, inuti vilka den anropar Repositorys suspend-funktioner. Resultatet omvandlas till StateFlow, som observeras av UI-lagret. Detta schema säkerställer tydlig ansvarsfördelning och testbarhet för varje lager oberoende.

Varför viewModelScope i ViewModel, inte i Fragment

Om koroutiner startades från Fragment skulle de vid skärmrotation avbrytas tillsammans med Fragmentets förstöring. ViewModel överlever rotation, så koroutiner startade i dess scope fortsätter. Detta är den viktigaste fördelen med viewModelScope jämfört med lifecycleScope vid dataladdning.

Exempel på användning av viewModelScope

Låt oss titta på tre praktiska scenarier för användning av viewModelScope i en Android-applikation i Kotlin.

Exempel 1: Ladda data när ViewModel skapas

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
        }
    }
}

I init-blocket startas omedelbart profilens laddning. Koroutinen körs på huvudtråden (standard). Repositoryt använder withContext(Dispatchers.IO) för nätverksförfrågan inuti sin suspend-funktion, så ViewModel behöver inte bry sig om trådväxling.

Exempel 2: Felhantering via 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")
        }
    }
}

UI-status beskrivs via sealed class UiState. ViewModel uppdaterar state vid varje ändring. Fragment prenumererar på StateFlow och reagerar endast på aktuell status och ignorerar föråldrade anrop vid upprepade rotationer.

Exempel 3: Avbrytning av föregående koroutin vid ny begäran

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
    }
}

Vid varje ny sökbegäran avbryts föregående koroutin. delay(300) implementerar debounce — sökning utförs först efter 300 ms paus i inmatningen. Detta minskar serverbelastningen och förhindrar föråldrade resultat.

viewModelScope vs lifecycleScope: när ska man välja vad

Båda scopen tillhandahålls av AndroidX Lifecycle-biblioteket men är kopplade till olika livscykler. Valet dem emellan beror på typen av uppgift.

Jämförelse av scopen

EgenskapviewModelScopelifecycleScope
ÄgareViewModelLifecycleOwner (Activity/Fragment)
Avbrytning vid rotationNej (ViewModel behålls)Ja (Activity återskapas)
StandarddispatcherDispatchers.Main.immediateDispatchers.Main.immediate
Tillgänglig iViewModelActivity, Fragment, Service
Typiskt scenarioDataladdning, affärslogikUI-interaktion, animationer, snackbars

Googles rekommendationer

Google rekommenderar att använda viewModelScope för alla uppgifter relaterade till dataladdning och bearbetning. lifecycleScope bör tillämpas för operationer kopplade till ett specifikt ögonblick i UI:ts liv — till exempel att starta en animation vid första skärmvisning eller prenumerera på Location-uppdateringar som ska stoppas när skärmen lämnas.

Vanliga misstag vid arbete med viewModelScope

Även i väldokumenterade Android API:er gör utvecklare karakteristiska misstag. Låt oss titta på fyra vanliga problem.

Misstag 1: Uppdatera UI efter avbrytning av scope

Det mest lömska misstaget — att försöka uppdatera StateFlow eller LiveData efter att ViewModel har rensats. Även om viewModelScope avbryts vid onCleared() kan koroutinen exekvera kod fram till den faktiska avbrytningen. Använd isActive för kontroll eller lita på att catch-blocket slutförs.

Misstag 2: Starta koroutiner utan hänsyn till SupervisorJob

viewModelScope använder internt SupervisorJob, vilket isolerar fel mellan koroutiner. Men om du startar en koroutin med egen Job() inuti viewModelScope.launch blir den koroutinen underordnad SupervisorJob men skyddas inte från avbrytning vid fel i andra koroutiner.

Misstag 3: För många koroutiner i ett scope

Även om viewModelScope inte har någon strikt gräns kan tusentals aktiva koroutiner sakta ner systemet. För långa datalistor, använd Flow med collectLatest istället för att skapa separata koroutiner för varje element.

Misstag 4: Användning av GlobalScope istället för viewModelScope

Om du av misstag importerar GlobalScope istället för viewModelScope kommer koroutinen inte att avbrytas vid rensning av ViewModel. Detta leder till minnesläckor och potentiell krasch. Kontrollera alltid att koroutiner startas via viewModelScope, särskilt i ärvda fragment.

Vanliga frågor

Kan man ändra viewModelScopes standarddispatcher?

ViewModelScopes dispatcher kan inte ändras direkt — den är hårt inställd som Dispatchers.Main.immediate. Men inuti en koroutin kan man via withContext växla till en annan dispatcher. För att ändra dispatcher i tester, använd TestDispatcher via Rule.

Hur skickar man viewModelScope till Repository?

Skicka inte scope till Repository — det bryter mot arkitekturprinciperna. Repository bör tillhandahålla suspend-funktioner och ViewModel hanterar själv koroutiner via viewModelScope. Om Repository kräver scope — omvärdera arkitekturen till förmån för Clean Architecture.

Varför använder viewModelScope SupervisorJob?

SupervisorJob garanterar att ett undantag i en koroutin (till exempel ett laddningsfel i en av flera oberoende förfrågningar) inte avbryter de andra koroutinerna. Detta överensstämmer med ViewModel-scenariot där olika skärmar laddar oberoende data.

Är viewModelScope tillgängligt i Jetpack Compose?

Ja, viewModelScope är tillgängligt i alla ViewModels oavsett UI-typ (View System eller Jetpack Compose). I Compose startas koroutiner också via viewModelScope och för UI-effekter används LaunchedEffect och rememberCoroutineScope.

Vad händer med koroutinen vid anrop av viewModelScope.cancel()?

Anrop av viewModelScope.cancel() avbryter scope omedelbart — alla aktiva koroutiner avslutas med CancellationException. Om du därefter anropar viewModelScope.launch skapas ett nytt scope automatiskt vid nästa åtkomst till gettern.

Sammanfattning

  • viewModelScope — CoroutineScope kopplat till ViewModels livscykel, avbryts automatiskt vid onCleared()
  • SupervisorJob + Dispatchers.Main — intern konfiguration som säkerställer felisolering och säker UI-åtkomst
  • Skärmrotation — ViewModel behålls, så koroutiner i viewModelScope fortsätter utan omstart
  • MVVM-arkitektur — viewModelScope är det centrala elementet för asynkrona operationer i ViewModel-lagret
  • lifecycleScope — alternativ för operationer kopplade till Activity/Fragments livscykel, inte ViewModels
  • StateFlow — föredraget sätt att överföra data från viewModelScopes koroutiner till UI via sealed class
  • GlobalScope är farligt — att ersätta viewModelScope med GlobalScope leder till minnesläckor och app-krascher

Vi utvecklar en mobil applikation nyckelfärdigt

IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.

Diskutera projektet

Läs också