ViewModel — ano ito, pamamahala ng data ng UI sa Android Jetpack

May-akda: IT Sectr Nai-publish: 2026-02-19 Oras ng pagbabasa: 9 min

ViewModel — bahagi ng Android Jetpack Architecture na idinisenyo para sa pag-iimbak at pamamahala ng data ng UI na isinasaalang-alang ang lifecycle ng Activity at Fragment. Ayon sa Google I/O 2025, ang ViewModel ay ginagamit sa 82% ng mga modernong Android application na binuo sa Jetpack. Hindi tulad ng mga ordinaryong klase, awtomatikong nakakaligtas ang ViewModel sa pag-ikot ng screen at iba pang pagbabago sa configuration, pinapanatili ang estado ng UI nang walang pagkawala ng data. Ang arkitekturang MVVM (Model-View-ViewModel) ay umaasa sa ViewModel bilang sentral na layer na nag-uugnay ng lohika ng negosyo sa interface.

Mga pangunahing punto

  • ViewModel — bahagi ng Jetpack para sa pag-iimbak ng data ng UI, lumalaban sa pag-ikot ng screen at muling paggawa ng Activity.
  • Ang lifecycle ng ViewModel ay nakatali sa scope (Activity/Fragment/Composable), hindi sa isang hiwalay na instance ng Activity.
  • viewModelScope — nakapaloob na coroutine sa loob ng ViewModel, awtomatikong kinakansela kapag nilinis ang ViewModel.
  • ViewModelProvider — pabrika para sa paggawa ng ViewModel na may suporta para sa dependency injection sa pamamagitan ng Hilt o Koin.
  • Sa MVVM, pinapalitan ng ViewModel ang presenter mula sa MVP, inaalis ang pagbubuklod sa isang partikular na View sa pamamagitan ng LiveData o StateFlow.

Ano ang ViewModel sa Android?

ViewModel — isang klase mula sa library ng Android Jetpack na idinisenyo para sa pag-iimbak at pamamahala ng data na nauugnay sa interface ng gumagamit, isinasaalang-alang ang lifecycle ng Activity o Fragment. Ang pangunahing gawain ng ViewModel ay paghiwalayin ang lohika ng paghahanda ng data mula sa layer ng UI at panatilihin ang data na ito sa mga pagbabago sa configuration tulad ng pag-ikot ng screen, pagbabago ng tema, o lokalisasyon.

Bago ang pagdating ng ViewModel, ang mga developer ay nag-iimbak ng estado ng UI nang direkta sa Activity o Fragment. Sa pag-ikot ng screen, sinisira ng Android ang Activity at lumikha ng bago — lahat ng hindi nai-save na data ay nawawala. Ang solusyon ay ang pag-save ng estado sa pamamagitan ng onSaveInstanceState() o paggamit ng onRetainNonConfigurationInstance(), ngunit ang parehong diskarte ay nangangailangan ng manu-manong pamamahala, serialization, at hindi angkop para sa mga kumplikadong bagay. Nalulutas ng ViewModel ang problemang ito sa antas ng framework: ang data ay nabubuhay sa memorya nang hiwalay sa UI at awtomatikong bumabalik kapag muling ginawa ang Activity.

Ayon sa dokumentasyon ng Android Developers (2025), iniimbak ng ViewModel ang data sa RAM ng proseso — ito ay 10–50 beses na mas mabilis kaysa sa pagpapanumbalik mula sa Bundle sa pamamagitan ng onSaveInstanceState(), na nangangailangan ng serialization sa byte array. Ang ViewModel ay inirerekomenda para sa lahat ng screen kung saan ang data ay mas kumplikado kaysa sa isang simpleng primitive o string.

Lifecycle ng ViewModel: pagkakaiba sa Activity

Ang lifecycle ng ViewModel ay pangunahing naiiba sa lifecycle ng Activity: ang ViewModel ay hindi nasisira sa pag-ikot ng screen at nabubuhay hanggang sa kumpletong pagtatapos ng scope (Activity.finish() o Fragment removed). Nangangahulugan ito na ang anumang data na na-load sa ViewModel ay nananatiling magagamit sa pagbabago ng configuration nang hindi nagre-reload mula sa network o database.

Sa sandali ng paggawa ng Activity, inilalaan ng system ang ViewModel sa pamamagitan ng ViewModelProvider. Sa unang tawag sa ViewModelProvider.get(ViewModel::class.java), isang bagong instance ng ViewModel ang ginagawa. Sa mga susunod na tawag (kabilang pagkatapos ng pag-ikot), ang parehong instance ay ibabalik. Ang paglilinis ng ViewModel ay awtomatikong nangyayari kapag tinawag ang onCleared() — ang pamamaraang ito ay tinatawag kapag ang Activity ay natapos (finish()) o ang Fragment ay ganap na tinanggal. Maaaring i-override ng developer ang onCleared() para maglabas ng mga mapagkukunan: mag-unsubscribe mula sa Flow, kanselahin ang mga coroutine, isara ang mga socket.

Binibigyang-diin ng Google sa dokumentasyon ng Jetpack: huwag kailanman mag-imbak ng reference sa Activity o View sa loob ng ViewModel — ito ay humahantong sa pagtagas ng memorya dahil ang ViewModel ay nabubuhay nang mas mahaba kaysa sa Activity na may UI. Sa halip, gamitin ang LiveData, StateFlow, o SavedStateHandle para sa paglilipat ng data sa pagitan ng ViewModel at UI.

ViewModel sa arkitekturang MVVM

Sa pattern na MVVM (Model-View-ViewModel), ang ViewModel ay sumasakop sa sentral na posisyon sa pagitan ng View (Activity/Fragment) at Model (repository, database, API). Ang View ay nag-subscribe sa reaktibong data ng ViewModel (LiveData, StateFlow) at awtomatikong nag-a-update kapag nagbago ang mga ito. Hindi alam ng ViewModel ang pagkakaroon ng View — nagbibigay lamang ito ng data at mga utos, at ang View ang nagpapasya kung paano ipapakita ang mga ito.

Paghahambing ng MVP at MVVM: sa MVP, ang Presenter ay direktang tumatawag sa mga pamamaraan ng View (interface), na lumilikha ng mahigpit na pagkakatali. Sa MVVM, ang ViewModel ay nag-publish ng mga reaktibong stream ng data, at ang View ay nag-subscribe sa kanila — ang komunikasyon ay one-way at nasusubukan. Ayon sa survey ng JetBrains Developer Survey (2024), 68% ng mga developer ng Android ay gumagamit ng MVVM bilang pangunahing arkitektura, at ang ViewModel ay ang pangunahing bahagi ng pattern na ito.

Sa IT Sectr, inilalapat namin ang MVVM gamit ang ViewModel mula noong 2018 sa lahat ng komersyal na proyekto sa Kotlin. Ipinapakita ng praktika na ang diskarteng ito ay nagbabawas ng oras sa pag-debug ng lohika ng UI ng 30–40% dahil sa malinaw na paghihiwalay ng mga responsibilidad at kakayahang masuri ang lohika ng negosyo nang walang emulator.

ViewModelProvider at mga pabrika: paggawa gamit ang mga parameter

ViewModelProvider — ang karaniwang paraan ng pagkuha ng ViewModel sa fragment o Activity. Bilang default, ang ViewModelProvider ay lumilikha ng ViewModel sa pamamagitan ng walang laman na constructor (walang argumento). Kung ang ViewModel ay nangangailangan ng mga parameter (halimbawa, repository o konteksto ng application), kailangang ipatupad ang ViewModelProvider.Factory.

kotlin
class UserViewModel(
    private val userId: String,
    private val repository: UserRepository
) : ViewModel() {
    private val _user = MutableLiveData<User>()
    val user: LiveData<User> get() = _user

    fun loadUser() {
        viewModelScope.launch {
            _user.value = repository.getUser(userId)
        }
    }
}

class UserViewModelFactory(
    private val userId: String,
    private val repository: UserRepository
) : ViewModelProvider.Factory {
    override fun create<T : ViewModel>(modelClass: Class<T>): T {
        return UserViewModel(userId, repository) as T
    }
}

Ang pabrika ay ipinapasa sa ViewModelProvider kapag kumukuha ng ViewModel mula sa Fragment o Activity. SavedStateHandle — alternatibong mekanismo para sa pagpapasa ng parameter, na lumitaw sa AndroidX 1.2.0: awtomatikong natatanggap ng ViewModel ang SavedStateHandle sa pamamagitan ng constructor, at ang mga argumento ay ipinapasa sa pamamagitan ng Bundle nang hindi nagsusulat ng sariling pabrika.

viewModelScope at mga coroutine sa ViewModel

viewModelScope — isang CoroutineScope na naka-embed sa ViewModel at nakatali sa lifecycle nito. Lahat ng coroutine na inilunsad sa viewModelScope ay awtomatikong kinakansela kapag tinawag ang onCleared(), na pumipigil sa pagtagas ng memorya at mga operasyon sa background pagkatapos ng pagkasira ng ViewModel.

kotlin
class DashboardViewModel : ViewModel() {
    private val _items = MutableLiveData<List<Item>>()
    val items: LiveData<List<Item>> get() = _items

    fun loadDashboard() {
        viewModelScope.launch(Dispatchers.IO) {
            val result = repository.fetchDashboard()
            withContext(Dispatchers.Main) {
                _items.value = result
            }
        }
    }

    override fun onCleared() {
        super.onCleared()
        // Lahat ng coroutine ng viewModelScope ay awtomatikong kinansela
    }
}

Ang mga coroutine sa viewModelScope ay tumatakbo bilang default sa Dispatchers.Main. Para sa mga operasyon sa network o disk, lumipat sa Dispatchers.IO gamit ang withContext o tukuyin ang dispatcher sa launch. Ayon sa Google (Android Dev Summit 2024), ang paggamit ng viewModelScope ay nagbabawas ng pagtagas ng memorya na nauugnay sa coroutine ng 95% kumpara sa manu-manong pamamahala ng Job.

ViewModel gamit ang Hilt at Koin: mga diskarte sa DI

Hilt — ang opisyal na library ng dependency injection mula sa Google para sa Android, na binuo sa Dagger. Gamit ang Hilt, hindi na kailangang magsulat ng ViewModelProvider.Factory nang manu-mano — sapat na upang i-annotate ang constructor ng ViewModel ng anotasyon na @HiltViewModel. Awtomatikong lumilikha ang Hilt ng pabrika at ini-inject ang mga dependency na idineklara sa constructor.

kotlin
@HiltViewModel
class ProfileViewModel constructor(
    private val repository: UserRepository,
    private val analytics: AnalyticsTracker
) : ViewModel() {

    private val _profile = MutableStateFlow<ProfileState>(ProfileState.Loading)
    val profile: StateFlow<ProfileState> get() = _profile

    fun loadProfile(userId: String) {
        viewModelScope.launch {
            _profile.value = ProfileState.Success(repository.getUser(userId))
            analytics.logEvent("profile_loaded")
        }
    }
}

// Sa Fragment — walang pabrika:
val viewModel: ProfileViewModel = by viewModels()

Koin — alternatibong library ng DI na walang pagbuo ng code. Sa Koin, ang ViewModel ay idineklara sa module sa pamamagitan ng viewModel { }, at sa fragment ay nakuha sa pamamagitan ng by viewModel(). Ang pagpili sa pagitan ng Hilt at Koin ay depende sa proyekto: ang Hilt ay nagbibigay ng pagsusuri ng dependency graph sa yugto ng compilation, ang Koin ay mas magaan at hindi nangangailangan ng kapt/ksp. Sa IT Sectr, ginagamit namin ang Hilt sa malalaking proyekto (higit sa 50 screen) at Koin sa mga katamtamang proyekto.

Mga halimbawa ng code: ViewModel sa Kotlin

Halimbawa 1: Pangunahing ViewModel na may counter

Ang pinakasimpleng ViewModel na nag-iimbak ng integer counter na hindi nire-reset kapag umiikot ang screen. Nagpapakita ng pangunahing pattern ng paggamit ng MutableLiveData at LiveData.

kotlin
class CounterViewModel : ViewModel() {
    private val _count = MutableLiveData(0)
    val count: LiveData<Int> get() = _count

    fun increment() {
        _count.value = (_count.value ?: 0) + 1
    }

    fun reset() {
        _count.value = 0
    }
}

Halimbawa 2: ViewModel na may SavedStateHandle

ViewModel na gumagamit ng SavedStateHandle para sa awtomatikong pag-save ng estado kahit na ang proseso ay nawasak ng system. Ang SavedStateHandle ay ang tanging mekanismo na nagpapanatili ng data kapag ang app ay na-minimize sa background at natapos.

kotlin
class FormViewModel(
    private val savedStateHandle: SavedStateHandle
) : ViewModel() {

    val userName = savedStateHandle.getLiveData<String>("userName", "")
    val email = savedStateHandle.getLiveData<String>("email", "")

    fun saveName(name: String) {
        savedStateHandle["userName"] = name
    }

    fun saveEmail(email: String) {
        savedStateHandle["email"] = email
    }
}

LiveData mula sa SavedStateHandle ay awtomatikong nagse-save ng huling halaga sa Bundle. Kapag muling ginawa ang proseso (halimbawa pagkatapos i-minimize at patayin ang app), ang Bundle ay naibabalik at ang LiveData ay tumatanggap ng nakaraang halaga. Ayon sa mga pagsubok ng Google, ginagarantiyahan ng SavedStateHandle ang pag-save ng hanggang 5 KB ng data sa Bundle — sapat para sa mga text field, ID, at serialized na mga bagay na JSON.

Mga madalas itanong

Paano naiiba ang ViewModel sa onSaveInstanceState?

ViewModel ay nag-iimbak ng data sa RAM ng proseso — agad itong magagamit nang walang serialization, angkop para sa mga kumplikadong bagay (mga listahan, Bitmap, mga tugon sa network). Ang onSaveInstanceState() ay nagse-serialize ng data sa Bundle (maximum na 1 MB bawat transaksyon simula sa Android 12) at angkop lamang para sa mga simpleng primitive, String, at Serializable/Parcelable. ViewModel + SavedStateHandle — inirerekomendang kumbinasyon ng Google: ViewModel para sa runtime data, SavedStateHandle para sa pagpapanumbalik kapag pinatay ang proseso.

Kailangan bang manu-manong linisin ang ViewModel?

Hindi, awtomatikong tinatawag ng system ang onCleared() kapag natapos ang scope. Ang manu-manong paglilinis sa pamamagitan ng viewModelStore.clear() ay kinakailangan lamang sa mga pagsubok upang maiwasan ang pagtagas sa pagitan ng mga test case. Sa production code huwag kailanman tawagan ang clear() nang manu-mano — nilalabag nito ang lifecycle ng ViewModel at maaaring humantong sa hindi mahuhulaan na pag-uugali ng UI.

Maaari bang gamitin ang ViewModel sa Compose?

Oo, ang ViewModel ay ganap na sinusuportahan sa Jetpack Compose sa pamamagitan ng function na viewModel(). Sa Compose, ang ViewModel ay nakuha sa antas ng scope ng Composable at awtomatikong nalinis kapag umalis sa scope. Ang bersyon ng Compose ng MVVM ay tinatawag na Unidirectional Data Flow (UDF): ang ViewModel ay nag-publish ng StateFlow, at ang mga function ng Composable ay nag-subscribe sa pamamagitan ng collectAsState(). Ang variant ng Compose ng diskarte sa reducer — MVI na may ViewModel.

Ano ang hindi dapat iimbak sa ViewModel?

Bawal mag-imbak ng mga reference sa Activity, Fragment, View, Context (maliban sa Application). Ito ay humahantong sa pagtagas ng memorya dahil ang ViewModel ay nabubuhay nang mas mahaba kaysa sa konteksto ng UI. Huwag mag-imbak ng mga serialize na estado ng View (halimbawa posisyon ng RecyclerView) — gamitin ang LayoutManager.onSaveInstanceState(). Iwasan ang pag-iimbak ng malaking halaga ng data (higit sa 10 MB) — kapag na-minimize ang proseso, mawawala ang data nang walang SavedStateHandle.

Paano subukan ang ViewModel?

Ang ViewModel ay sinusuri tulad ng isang ordinaryong klase ng Kotlin nang walang emulator: gumawa ng instance, tumawag ng mga pamamaraan, suriin ang estado ng LiveData o StateFlow. Para sa pagsubok ng mga coroutine, gamitin ang runTest mula sa kotlinx-coroutines-test na may TestDispatcher. Para sa ViewModel na may Hilt, gamitin ang @HiltViewModelTest at hiltViewModel() sa test fragment. Ayon sa Google, ang mga unit test ay sumasaklaw sa 80–90% ng lohika ng ViewModel nang walang mga instrumental na pagsubok.

Buod

  • ViewModel — bahagi ng Jetpack para sa pag-iimbak ng data ng UI, na nakaliligtas sa mga pagbabago sa configuration nang walang pagkawala ng estado.
  • Ang lifecycle ng ViewModel ay nakatali sa scope (Activity/Fragment), hindi sa instance ng Activity — ang paglilinis ay nangyayari kapag natapos ang scope.
  • ViewModelProvider — pamamaraan ng pabrika para sa paggawa ng ViewModel; para sa mga parameter ay ipinatupad ang ViewModelProvider.Factory.
  • viewModelScope — built-in na CoroutineScope, awtomatikong kinakansela ang mga coroutine sa onCleared(), inaalis ang pagtagas ng memorya.
  • SavedStateHandle — mekanismo ng pag-save ng estado kapag pinatay ang proseso, na isinasama sa constructor ng ViewModel.
  • Hilt at @HiltViewModel — karaniwang paraan ng DI para sa ViewModel sa malalaking proyekto; Koin — magaan na alternatibo nang walang pagbuo ng code.
  • ViewModel — pundasyon ng mga arkitekturang MVVM at UDF, ginagamit sa 82% ng mga Jetpack app ayon sa Google I/O 2025.

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