viewModelScope: mi ez, kapcsolódás a ViewModel-hez és működés Androidban

Szerző: IT Sectr Megjelenés: 2026-06-23 Olvasási idő: 9 perc

viewModelScope — egy beépített CoroutineScope az androidx.lifecycle könyvtárból, amely a ViewModel életciklusához van kötve és annak tisztításakor automatikusan törlődik. A Google Android Developers, 2025 szerint a viewModelScope a korutinok indításának szabványos mechanizmusa az MVVM architektúrában, biztonságos aszinkron műveleteket biztosítva memóriaszivárgás kockázata nélkül. A ViewModelScope alapértelmezésben Dispatchers.Main-t használ, és az összes IO műveletet benne a withContext-en keresztül kell végrehajtani.

Fő pontok

  • viewModelScope — CoroutineScope a lifecycle-viewmodel-ktx-ből, a ViewModel onCleared()-jekor törlődik
  • Dispatchers.Main — alapértelmezett dispatcher, ezért a korutinokon belüli UI frissítések biztonságosak
  • onCleared — callback, melynek meghívásakor a viewModelScope automatikusan törli az összes aktív korutint
  • clear() vs onCleared() — a clear() a framework által az onCleared előtt hívódik, garantálva a scope törlését
  • launch — a korutinok indításának fő módja a viewModelScope-ban fire-and-forget műveletekhez

Mi az a viewModelScope Androidban?

viewModelScope — egy kiterjesztési tulajdonság (extension property) a ViewModel interfészen, amely a lifecycle-viewmodel-ktx könyvtárban (2.1.0 verziótól) került hozzáadásra. Kész CoroutineScope-ot biztosít, amely a ViewModel életciklusához van kötve.

kotlin
// Belső struktúra (egyszerűsített)
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) }
    }

A scope lustán (lazy) jön létre az első hozzáféréskor, és a setTag-en keresztül gyorsítótárazódik. SupervisorJob kerül használatra, ami azt jelenti, hogy egy gyermek korutinban lévő kivétel nem törli a többit. Az alapértelmezett dispatcher a Dispatchers.Main.immediate, amely a főszálon hajtja végre a kódot további dispatch nélkül, ha a hívás már Main-en van.

Hogyan kap a viewModelScope értesítést a tisztításról

Amikor a ViewModel elhagyja az életciklust (Activity befejeződött vagy Fragment eltávolításra került), a rendszer meghívja a clear()-t, amely elindítja az onCleared()-et. Ebben a callback-ben a viewModelScope törli a Job-ját, ami rekurzívan befejezi az összes aktív korutint. A mechanizmus a Closeable interfészen keresztül van implementálva, ahol a scope Job-ja automatikus lezárásra szánt erőforrásként van regisztrálva.

Hogyan működik a viewModelScope: kapcsolódás a ViewModel életciklusához

A viewModelScope ViewModel életciklusához való kapcsolódásának mechanizmusa címkézésen és az onCleared callback-en alapul. Nézzük meg lépésről lépésre, hogyan működik.

1. lépés: A scope létrehozása az első hozzáféréskor

Amikor a ViewModel végrehajtja a viewModelScope.launch { ... }-t, a getter ellenőrzi, hogy létezik-e mentett scope a JOB_KEY címke alatt. Ha a scope még nem jött létre — létrejön egy új CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate) példány. A scope a ViewModel-en belül egy belső címke térképen keresztül tárolódik.

2. lépés: A korutinok életciklusa

A viewModelScope.launch vagy viewModelScope.async útján indított összes korutin a scope SupervisorJob-jának gyermekévé válik. A főszálon dolgoznak (ha nincs más dispatcher megadva a withContext-en keresztül). Amíg a ViewModel él — a korutinok lehetnek aktívak, felfüggesztettek vagy befejezettek.

3. lépés: Törlés az onCleared-nél

Amikor a rendszer megsemmisíti a ViewModel-t, ViewModel.clear() kerül meghívásra. A clear()-en belül a következő történik:

  • onCleared() meghívása a felhasználói logikához
  • Az összes addCloseable-en keresztül regisztrált Closeable erőforrás bezárása
  • A viewModelScope Job-ja Cancelled állapotba kerül
  • Az összes gyermek korutin rekurzívan törlődik
  • A scope-ra mutató referenciák felszabadítása a szemétgyűjtő számára

Ellenállás a képernyőforgatással szemben

Képernyőforgatáskor az Activity újra létrejön, de a ViewModel megmarad (a ViewModelStoreOwner-nek köszönhetően). Ez azt jelenti, hogy a viewModelScope aktív marad, és a korutinok megszakítás nélkül folytatódnak. Az Activity újralétrehozása után ugyanaz a ViewModel (és ugyanaz a scope) kerül felhasználásra — az adatbetöltés nem kezdődik újra.

viewModelScope az MVVM architektúrában

MVVM (Model-View-ViewModel) — a Google által ajánlott architektúra Android alkalmazásokhoz. A viewModelScope központi helyet foglal el benne az aszinkron műveletek végrehajtójaként.

A viewModelScope szerepe az architektúra rétegeiben

RétegKomponensA viewModelScope szerepe
UIActivity / FragmentFigyeli a StateFlow/LiveData-t a ViewModel-ből
ViewModelViewModelKorutinokat indít a viewModelScope-on keresztül, kezeli az UI állapotát
RepositoryRepositoryFogadja a viewModelScope korutinjaiból hívott suspend-függvényeket
DataDAO / ApiVégrehajtja a tényleges kéréseket (Room, Retrofit)

A ViewModel a viewModelScope-on keresztül korutinokat indít, amelyeken belül meghívja a Repository suspend-függvényeit. Az eredmény StateFlow-vá alakul, amelyet az UI réteg figyel. Ez a séma biztosítja a felelősségek egyértelmű elválasztását és az egyes rétegek független tesztelhetőségét.

Miért a ViewModel-ben van a viewModelScope, nem a Fragment-ben

Ha a korutinok a Fragment-ből indulnának, a képernyő forgatásakor a Fragment megsemmisítésével együtt törlődnének. A ViewModel túléli a forgatást, így a scope-jában indított korutinok folytatódnak. Ez a viewModelScope kulcsfontosságú előnye a lifecycleScope-pal szemben adatbetöltéskor.

Példák a viewModelScope használatára

Nézzünk meg három gyakorlati forgatókönyvet a viewModelScope használatára egy Android alkalmazásban Kotlin nyelven.

1. példa: Adatok betöltése a ViewModel létrehozásakor

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

Az init blokkban azonnal elindul a profil betöltése. A korutin alapértelmezésben a főszálon fut. A repository a withContext(Dispatchers.IO)-t használja a hálózati kéréshez a suspend-függvényén belül, így a ViewModel nem foglalkozik szálváltással.

2. példa: Hibakezelés sealed class segítségével

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

Az UI állapotot sealed class UiState írja le. A ViewModel minden változáskor frissíti a state-et. A Fragment fel van iratkozva a StateFlow-ra, és csak az aktuális állapotra reagál, figyelmen kívül hagyva az elavult hívásokat ismételt forgatásoknál.

3. példa: Az előző korutin törlése új kérésnél

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

Minden új keresési kérésnél az előző korutin törlődik. A delay(300) debounce-t valósít meg — a keresés csak 300 ms szünet után történik a bevitelben. Ez csökkenti a szerverterhelést és megakadályozza az elavult eredményeket.

viewModelScope vs lifecycleScope: mikor mit válasszunk

Mindkét scope-ot az AndroidX Lifecycle könyvtár biztosítja, de különböző életciklusokhoz vannak kötve. A választás a feladat típusától függ.

A scope-ok összehasonlítása

JellemzőviewModelScopelifecycleScope
TulajdonosViewModelLifecycleOwner (Activity/Fragment)
Törlés forgatáskorNem (ViewModel megmarad)Igen (Activity újra létrejön)
Alapértelmezett dispatcherDispatchers.Main.immediateDispatchers.Main.immediate
ElérhetőViewModelActivity, Fragment, Service
Tipikus forgatókönyvAdatbetöltés, üzleti logikaUI interakció, animációk, snackbar-ek

Google ajánlások

A Google a viewModelScope használatát ajánlja minden adatbetöltéssel és feldolgozással kapcsolatos feladathoz. A lifecycleScope-ot az UI életének egy adott pillanatához kötött műveletekhez kell alkalmazni — például animáció indításához a képernyő első megjelenésekor vagy Location frissítésekre való feliratkozáshoz, amelynek meg kell szűnnie a képernyő elhagyásakor.

Gyakori hibák a viewModelScope használatakor

Még a jól dokumentált Android API-ban is előfordulnak tipikus hibák. Nézzünk meg négy gyakori problémát.

1. hiba: UI frissítése a scope törlése után

A legveszélyesebb hiba — a StateFlow vagy LiveData frissítésének kísérlete a ViewModel tisztítása után. Bár a viewModelScope törlődik az onCleared()-ben, a korutin kódot hajthat végre a tényleges törlés pillanatáig. Használja az isActive-t ellenőrzéshez, vagy támaszkodjon a catch blokk befejeződésére.

2. hiba: Korutinok indítása a SupervisorJob figyelembevétele nélkül

A viewModelScope belsőleg SupervisorJob-ot használ, ami elkülöníti a hibákat a korutinok között. De ha saját Job()-jal indít egy korutint a viewModelScope.launch-on belül, az a korutin a SupervisorJob gyermekévé válik, de nem lesz védve a törléstől más korutinok hibái esetén.

3. hiba: Túl sok korutin egy scope-ban

Bár a viewModelScope-nak nincs szigorú korlátja, több ezer aktív korutin lelassíthatja a rendszert. Hosszú adatlistákhoz használjon Flow-t a collectLatest-tel ahelyett, hogy külön korutinokat hozna létre minden elemhez.

4. hiba: GlobalScope használata viewModelScope helyett

Ha véletlenül GlobalScope-ot importál a viewModelScope helyett, a korutin nem törlődik a ViewModel tisztításakor. Ez memóriaszivárgáshoz és potenciális összeomláshoz vezet. Mindig ellenőrizze, hogy a korutinok viewModelScope-on keresztül indulnak, különösen örökölt fragmensekben.

Gyakran Ismételt Kérdések

Meg lehet változtatni a viewModelScope alapértelmezett dispatcherét?

A viewModelScope dispatcherét közvetlenül nem lehet megváltoztatni — szigorúan Dispatchers.Main.immediate-ként van beállítva. De a korutinon belül a withContext-en keresztül át lehet váltani másik dispatcherre. A dispatcher tesztelés közbeni megváltoztatásához használja a TestDispatcher-t a Rule-on keresztül.

Hogyan adjuk át a viewModelScope-ot a Repository-nak?

Ne adja át a scope-ot a Repository-nak — ez sérti az architekturális elveket. A Repository-nak suspend-függvényeket kell biztosítania, a ViewModel pedig maga kezeli a korutinokat a viewModelScope-on keresztül. Ha a Repository scope-ot igényel — vizsgálja felül az architektúrát a Clean Architecture javára.

Miért használ a viewModelScope SupervisorJob-ot?

A SupervisorJob garantálja, hogy az egyik korutinban lévő kivétel (például több független kérés egyikének betöltési hibája) nem törli a többi korutint. Ez megfelel a ViewModel forgatókönyvének, ahol különböző képernyők független adatokat töltenek be.

Elérhető a viewModelScope Jetpack Compose-ban?

Igen, a viewModelScope elérhető bármely ViewModel-ben, függetlenül az UI típusától (View System vagy Jetpack Compose). A Compose-ban a korutinok szintén a viewModelScope-on keresztül indulnak, az UI effektekhez pedig LaunchedEffect és rememberCoroutineScope használatos.

Mi történik a korutinnal a viewModelScope.cancel() hívásakor?

A viewModelScope.cancel() hívása azonnal törli a scope-ot — az összes aktív korutin CancellationException-nel végződik. Ha ezután meghívja a viewModelScope.launch-ot, egy új scope automatikusan létrejön a getter következő hozzáférésénél.

Összefoglalás

  • viewModelScope — a ViewModel életciklusához kötött CoroutineScope, automatikusan törlődik onCleared()-ben
  • SupervisorJob + Dispatchers.Main — belső konfiguráció, amely biztosítja a hibák elkülönítését és a biztonságos UI hozzáférést
  • Képernyőforgatás — a ViewModel megmarad, így a viewModelScope-ban lévő korutinok újraindítás nélkül folytatódnak
  • MVVM architektúra — a viewModelScope a központi elem az aszinkron műveletekhez a ViewModel rétegben
  • lifecycleScope — alternatíva az Activity/Fragment életciklusához kötött műveletekhez, nem a ViewModel-éhez
  • StateFlow — preferált adatátviteli mód a viewModelScope korutinjaiból az UI-ba sealed class segítségével
  • A GlobalScope veszélyes — a viewModelScope GlobalScope-ra cserélése memóriaszivárgáshoz és alkalmazás-összeomláshoz vezet

Kulcsrakész mobilalkalmazást fejlesztünk

Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.

Projekt megbeszélése

Olvassa el is