viewModelScope: ano ito, pagkakaugnay sa ViewModel at trabaho sa Android

May-akda: IT Sectr Nai-publish: 2026-06-23 Oras ng pagbabasa: 9 min

viewModelScope — ay isang built-in na CoroutineScope mula sa androidx.lifecycle library, na nakatali sa lifecycle ng ViewModel at awtomatikong kinakansela kapag ito ay nilinis. Ayon sa Google Android Developers, 2025, ang viewModelScope ay ang karaniwang mekanismo para sa pagpapatakbo ng mga coroutine sa MVVM architecture, na nagbibigay ng ligtas na trabaho sa mga asynchronous na operasyon nang walang panganib ng memory leak. Ang ViewModelScope ay gumagamit ng Dispatchers.Main bilang default, at lahat ng IO operations sa loob nito ay dapat isagawa sa pamamagitan ng withContext.

Mga pangunahing punto

  • viewModelScope — CoroutineScope mula sa lifecycle-viewmodel-ktx, kinakansela sa onCleared() ng ViewModel
  • Dispatchers.Main — default na dispatcher, kaya ang mga UI update sa loob ng coroutine ay ligtas
  • onCleared — callback, kapag tinawag awtomatikong kinakansela ng viewModelScope ang lahat ng aktibong coroutine
  • clear() vs onCleared() — clear() ay tinatawag ng framework bago ang onCleared, na ginagarantiya ang pagkansela ng scope
  • launch — pangunahing paraan ng pagpapatakbo ng coroutine sa viewModelScope para sa fire-and-forget operations

Ano ang viewModelScope sa Android?

viewModelScope — ay isang extension property sa ViewModel interface, idinagdag sa lifecycle-viewmodel-ktx library (mula bersyon 2.1.0). Nagbibigay ito ng handa nang CoroutineScope na nakatali sa lifecycle ng ViewModel.

kotlin
// Panloob na istraktura (pinasimple)
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) }
    }

Ang scope ay ginagawa nang tamad (lazy) sa unang pag-access at naka-cache sa pamamagitan ng setTag. Ginagamit ang SupervisorJob, na nangangahulugang ang exception sa isang child coroutine ay hindi kinakansela ang iba. Ang default na dispatcher ay Dispatchers.Main.immediate, na nagpapatupad ng code sa main thread nang walang karagdagang dispatch kung ang tawag ay nasa Main na.

Paano nakakatanggap ang viewModelScope ng notification ng paglilinis

Kapag ang ViewModel ay umalis sa lifecycle (Activity natapos o Fragment naalis), ang sistema ay tumatawag ng clear(), na nagti-trigger ng onCleared(). Sa callback na ito, kinakansela ng viewModelScope ang Job nito, na rekursibong nagtatapos sa lahat ng aktibong coroutine. Ang mekanismo ay ipinatupad sa pamamagitan ng Closeable interface, kung saan ang Job ng scope ay nirerehistro bilang isang resource para sa awtomatikong pagsasara.

Paano gumagana ang viewModelScope: pagkakaugnay sa lifecycle ng ViewModel

Ang mekanismo ng pagtali ng viewModelScope sa lifecycle ng ViewModel ay batay sa pag-tag at callback na onCleared. Suriin natin hakbang-hakbang kung paano ito gumagana.

Hakbang 1: Paggawa ng scope sa unang pag-access

Kapag ang ViewModel ay nag-execute ng viewModelScope.launch { ... }, sinusuri ng getter kung may naka-save na scope sa ilalim ng tag na JOB_KEY. Kung ang scope ay hindi pa nagagawa — isang bagong instance ng CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate) ang gagawin. Ang scope ay iniimbak sa loob ng ViewModel sa pamamagitan ng internal na tag map.

Hakbang 2: Lifecycle ng coroutine

Lahat ng coroutine na pinatakbo sa pamamagitan ng viewModelScope.launch o viewModelScope.async ay nagiging anak ng SupervisorJob ng scope. Gumagana ang mga ito sa main thread (kung walang ibang dispatcher na tinukoy sa pamamagitan ng withContext). Habang buhay ang ViewModel — ang mga coroutine ay maaaring aktibo, naka-suspend, o tapos na.

Hakbang 3: Pagkansela sa onCleared

Kapag sinira ng sistema ang ViewModel, ViewModel.clear() ang tinatawag. Sa loob ng clear() ay nangyayari ang sumusunod:

  • Tawag sa onCleared() para sa user logic
  • Pagsasara ng lahat ng Closeable resources na nirehistro sa pamamagitan ng addCloseable
  • Job ng viewModelScope ay pumupunta sa status na Cancelled
  • Lahat ng child coroutine ay rekursibong kinakansela
  • Pagpapalaya ng mga reference sa scope para sa garbage collector

Paglaban sa screen rotation

Sa pag-ikot ng screen, ang Activity ay muling ginagawa, ngunit ang ViewModel ay pinapanatili (salamat sa ViewModelStoreOwner). Nangangahulugan ito na ang viewModelScope ay nananatiling aktibo, at ang mga coroutine ay patuloy na tumatakbo nang walang pagkaantala. Pagkatapos ng muling paggawa ng Activity, ang parehong ViewModel (at ang parehong scope) ay ginagamit muli — ang pag-load ng data ay hindi nagsisimula muli.

viewModelScope sa arkitekturang MVVM

MVVM (Model-View-ViewModel) — ay ang inirerekomendang arkitektura ng Google para sa Android applications. Ang viewModelScope ay sumasakop sa sentral na lugar dito bilang tagapagpatupad ng mga asynchronous na operasyon.

Papel ng viewModelScope sa mga layer ng arkitektura

LayerComponentPapel ng viewModelScope
UIActivity / FragmentNagmamasid ng StateFlow/LiveData mula sa ViewModel
ViewModelViewModelNagpapatakbo ng coroutine sa pamamagitan ng viewModelScope, namamahala ng UI state
RepositoryRepositoryTumatanggap ng suspend functions na tinatawag mula sa viewModelScope coroutine
DataDAO / ApiNagsasagawa ng aktwal na mga request (Room, Retrofit)

Ang ViewModel sa pamamagitan ng viewModelScope ay nagpapatakbo ng coroutine, sa loob nito ay tumatawag ng suspend functions ng Repository. Ang resulta ay ginagawang StateFlow, na minamasdan ng UI layer. Ang scheme na ito ay nagsisiguro ng malinaw na paghihiwalay ng mga responsibilidad at testability ng bawat layer nang independyente.

Bakit viewModelScope sa ViewModel, hindi sa Fragment

Kung ang mga coroutine ay pinatakbo mula sa Fragment, sa pag-ikot ng screen sila ay kakanselahin kasama ng pagkawasak ng Fragment. Ang ViewModel ay nakaliligtas sa rotation, kaya ang mga coroutine na pinatakbo sa scope nito ay patuloy na tumatakbo. Ito ang pangunahing bentahe ng viewModelScope kumpara sa lifecycleScope sa pag-load ng data.

Mga halimbawa ng paggamit ng viewModelScope

Tingnan natin ang tatlong praktikal na senaryo ng paggamit ng viewModelScope sa Android application sa Kotlin.

Halimbawa 1: Pag-load ng data sa paggawa ng 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
        }
    }
}

Sa init block, agad na sinisimulan ang pag-load ng profile. Ang coroutine ay tumatakbo sa main thread (bilang default). Ang repository ay gumagamit ng withContext(Dispatchers.IO) para sa network request sa loob ng suspend function nito, kaya ang ViewModel ay hindi nag-aalala tungkol sa paglipat ng thread.

Halimbawa 2: Paghawak ng error sa pamamagitan ng 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")
        }
    }
}

Ang UI state ay inilalarawan sa pamamagitan ng sealed class UiState. Ina-update ng ViewModel ang state sa bawat pagbabago. Ang Fragment ay naka-subscribe sa StateFlow at tumutugon lamang sa kasalukuyang estado, hindi pinapansin ang mga lumang tawag sa paulit-ulit na rotation.

Halimbawa 3: Pagkansela ng nakaraang coroutine sa bagong request

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

Sa bawat bagong search request, ang nakaraang coroutine ay kinakansela. Ang delay(300) ay nagpapatupad ng debounce — ang paghahanap ay isinasagawa lamang pagkatapos ng 300 ms na pause sa input. Binabawasan nito ang server load at pinipigilan ang mga lumang resulta.

viewModelScope vs lifecycleScope: kailan pipiliin ang ano

Ang parehong scope ay ibinibigay ng AndroidX Lifecycle library, ngunit nakatali sa magkaibang lifecycle. Ang pagpili sa pagitan nila ay depende sa uri ng gawain.

Paghahambing ng scope

KatangianviewModelScopelifecycleScope
May-ariViewModelLifecycleOwner (Activity/Fragment)
Pagkansela sa rotationHindi (ViewModel pinapanatili)Oo (Activity muling ginagawa)
Default na dispatcherDispatchers.Main.immediateDispatchers.Main.immediate
Available saViewModelActivity, Fragment, Service
Karaniwang senaryoPag-load ng data, business logicUI interaction, animation, snackbar

Rekomendasyon ng Google

Inirerekomenda ng Google ang paggamit ng viewModelScope para sa lahat ng gawain na may kaugnayan sa pag-load at pagproseso ng data. Ang lifecycleScope ay dapat ilapat para sa mga operasyon na nakatali sa isang tiyak na sandali ng UI life — halimbawa, pagsisimula ng animation sa unang paglitaw ng screen o pag-subscribe sa Location updates na dapat huminto sa pag-alis ng screen.

Mga karaniwang pagkakamali sa paggamit ng viewModelScope

Kahit sa mahusay na dokumentadong API ng Android, ang mga developer ay gumagawa ng mga katangiang pagkakamali. Tingnan natin ang apat na pinakakaraniwang problema.

Pagkakamali 1: Pag-update ng UI pagkatapos ng pagkansela ng scope

Ang pinaka-mapanganib na pagkakamali — pagsubok na i-update ang StateFlow o LiveData pagkatapos malinis ang ViewModel. Kahit na ang viewModelScope ay kinakansela sa onCleared(), ang coroutine ay maaaring mag-execute ng code hanggang sa aktwal na pagkansela. Gamitin ang isActive para sa pagsusuri o umasa sa pagkumpleto ng catch block.

Pagkakamali 2: Pagpapatakbo ng coroutine nang hindi isinasaalang-alang ang SupervisorJob

Ang viewModelScope ay panloob na gumagamit ng SupervisorJob, na naghihiwalay ng mga error sa pagitan ng coroutine. Ngunit kung magpapatakbo ka ng coroutine na may sariling Job() sa loob ng viewModelScope.launch, ang coroutine na iyon ay magiging anak ng SupervisorJob, ngunit hindi mapoprotektahan mula sa pagkansela sa mga error sa ibang coroutine.

Pagkakamali 3: Masyadong maraming coroutine sa isang scope

Kahit na ang viewModelScope ay walang mahigpit na limitasyon, libu-libong aktibong coroutine ang maaaring makapagpabagal ng system. Para sa mahabang listahan ng data, gamitin ang Flow na may collectLatest sa halip na gumawa ng hiwalay na coroutine para sa bawat elemento.

Pagkakamali 4: Paggamit ng GlobalScope sa halip na viewModelScope

Kung hindi sinasadyang mag-import ng GlobalScope sa halip na viewModelScope, ang coroutine ay hindi makakansela sa paglilinis ng ViewModel. Ito ay humahantong sa memory leak at potensyal na crash. Palaging suriin na ang mga coroutine ay pinapatakbo sa pamamagitan ng viewModelScope, lalo na sa mga inherited fragment.

Mga Madalas Itanong

Maaari bang baguhin ang default na dispatcher ng viewModelScope?

Hindi maaaring direktang baguhin ang dispatcher ng viewModelScope — ito ay nakatakda bilang Dispatchers.Main.immediate. Ngunit sa loob ng coroutine ay maaaring lumipat sa ibang dispatcher sa pamamagitan ng withContext. Para sa pagpapalit ng dispatcher sa mga test, gamitin ang TestDispatcher sa pamamagitan ng Rule.

Paano ipasa ang viewModelScope sa Repository?

Huwag ipasa ang scope sa Repository — ito ay lumalabag sa mga prinsipyo ng arkitektura. Ang Repository ay dapat magbigay ng suspend functions, at ang ViewModel mismo ang namamahala ng coroutine sa pamamagitan ng viewModelScope. Kung ang Repository ay nangangailangan ng scope — suriin muli ang arkitektura pabor sa Clean Architecture.

Bakit gumagamit ng SupervisorJob ang viewModelScope?

Ginagarantiya ng SupervisorJob na ang exception sa isang coroutine (halimbawa, error sa pag-load ng isa sa maraming independyenteng request) ay hindi kinakansela ang iba pang coroutine. Ito ay tumutugma sa senaryo ng ViewModel, kung saan ang iba't ibang screen ay naglo-load ng independyenteng data.

Available ba ang viewModelScope sa Jetpack Compose?

Oo, available ang viewModelScope sa anumang ViewModel, anuman ang uri ng UI (View System o Jetpack Compose). Sa Compose, ang mga coroutine ay pinapatakbo din sa pamamagitan ng viewModelScope, at para sa UI effects ay ginagamit ang LaunchedEffect at rememberCoroutineScope.

Ano ang mangyayari sa coroutine kapag tinawag ang viewModelScope.cancel()?

Ang pagtawag ng viewModelScope.cancel() ay kinakansela ang scope agad — lahat ng aktibong coroutine ay nagtatapos sa CancellationException. Kung pagkatapos nito ay tumawag ka ng viewModelScope.launch, isang bagong scope ang awtomatikong gagawin sa susunod na pag-access sa getter.

Buod

  • viewModelScope — CoroutineScope na nakatali sa lifecycle ng ViewModel, awtomatikong kinakansela sa onCleared()
  • SupervisorJob + Dispatchers.Main — panloob na configuration na nagsisiguro ng error isolation at ligtas na access sa UI
  • Screen rotation — ViewModel pinapanatili, kaya ang coroutine sa viewModelScope ay nagpapatuloy nang walang restart
  • MVVM architecture — viewModelScope ang sentral na elemento para sa asynchronous operations sa ViewModel layer
  • lifecycleScope — alternatibo para sa operations na nakatali sa lifecycle ng Activity/Fragment, hindi ViewModel
  • StateFlow — mas gustong paraan ng paglipat ng data mula sa viewModelScope coroutine patungo sa UI sa pamamagitan ng sealed class
  • Mapanganib ang GlobalScope — ang pagpapalit ng viewModelScope ng GlobalScope ay humahantong sa memory leak at pag-crash ng app

Gagawa kami ng mobile application na turnkey

Gumagawa ang IT Sectr ng mga iOS at Android application para sa mga startup at negosyo mula noong 2017. Magpapayo kami sa iyo at magmumungkahi ng pinakamahusay na solusyon.

Pag-usapan ang proyekto

Basahin din