ViewModel — шта је то, управљање UI подацима у Android Jetpack

Аутор: IT Sectr Објављено: 2026-02-19 Време читања: 9 мин

ViewModel — компонента Android Jetpack Architecture намењена за чување и управљање UI подацима узимајући у обзир животни циклус Activity и Fragment. Према Google I/O 2025, ViewModel се користи у 82% модерних Android апликација изграђених на Jetpack. За разлику од обичних класа, ViewModel аутоматски преживљава ротацију екрана и друге конфигурационе промене, чувајући стање UI без губитка података. Архитектура MVVM (Model-View-ViewModel) ослања се на ViewModel као централни слој који повезује пословну логику са интерфејсом.

Главно

  • ViewModel — Jetpack компонента за чување UI података, отпорна на ротацију екрана и поновно креирање Activity.
  • Животни циклус ViewModel везан је за scope (Activity/Fragment/Composable), а не за појединачну инстанцу Activity.
  • viewModelScope — уграђена корутина унутар ViewModel, аутоматски отказана при чишћењу ViewModel.
  • ViewModelProvider — фабрика за креирање ViewModel са подршком за dependency injection кроз Hilt или Koin.
  • У MVVM ViewModel замењује презентер из MVP, ослобађајући од везивања за конкретан View кроз LiveData или StateFlow.

Шта је ViewModel у Android?

ViewModel — класа из библиотеке Android Jetpack намењена за чување и управљање подацима повезаним са корисничким интерфејсом, узимајући у обзир животни циклус Activity или Fragment. Главни задатак ViewModel је да одвоји логику припреме података од UI слоја и сачува те податке при конфигурационим променама, као што су ротација екрана, промена теме или локализације.

Пре појаве ViewModel, програмери су чували стање UI директно у Activity или Fragment. При ротацији екрана, Android уништава Activity и ствара ново — сви несачувани подаци су се губили. Решење је било чување стања кроз onSaveInstanceState() или коришћење onRetainNonConfigurationInstance(), али оба приступа су захтевала ручно управљање, серијализацију и нису била погодна за сложене објекте. ViewModel решава овај проблем на нивоу фрејмворка: подаци живе у меморији одвојено од UI и аутоматски се враћају при поновном креирању Activity.

Према документацији Android Developers (2025), ViewModel чува податке у RAM меморији процеса — то је 10–50 пута брже него обнављање из Bundle-а кроз onSaveInstanceState(), где је потребна серијализација у бајт низ. ViewModel се препоручује за све екране где су подаци сложенији од простог примитива или ниске.

Животни циклус ViewModel: разлика од Activity

Животни циклус ViewModel се суштински разликује од животног циклуса Activity: ViewModel се не уништава при ротацији екрана и живи до потпуног завршетка scope-а (Activity.finish() или Fragment removed). То значи да сви подаци учитани у ViewModel остају доступни при промени конфигурације без поновног учитавања из мреже или базе података.

У тренутку креирања Activity, систем додељује ViewModel кроз ViewModelProvider. При првом позиву ViewModelProvider.get(ViewModel::class.java) креира се нова инстанца ViewModel. При поновљеним позивима (укључујући и после ротације) враћа се иста инстанца. Чишћење ViewModel се дешава аутоматски при позиву onCleared() — овај метод се позива када се Activity завршава (finish()) или Fragment потпуно уклања. Програмер може да прегази onCleared() ради ослобађања ресурса: одјава са Flow, отказивање корутина, затварање сокета.

Google у документацији Jetpack наглашава: никада не чувајте референцу на Activity или View унутар ViewModel — то доводи до цурења меморије, јер ViewModel надживљава Activity са UI. Уместо тога користите LiveData, StateFlow или SavedStateHandle за пренос података између ViewModel и UI.

ViewModel у архитектури MVVM

У обрасцу MVVM (Model-View-ViewModel) ViewModel заузима централно место између View (Activity/Fragment) и Model (репозиторијум, база података, API). View се претплаћује на реактивне податке ViewModel (LiveData, StateFlow) и аутоматски се ажурира при њиховој промени. ViewModel не зна за постојање View — он само пружа податке и команде, а View одлучује како да их прикаже.

Поређење MVP и MVVM: у MVP, Presenter директно позива методе View (интерфејса), стварајући чврсту везу. У MVVM, ViewModel објављује реактивне токове података, а View се претплаћује на њих — комуникација је једносмерна и тестирабилна. Према анкети JetBrains Developer Survey (2024), 68% Android програмера користи MVVM као главну архитектуру, а ViewModel је кључна компонента овог обрасца.

У IT Sectr, примењујемо MVVM са ViewModel од 2018. године у свим комерцијалним пројектима на Kotlin. Практика показује да овај приступ смањује време отклањања грешака у UI логици за 30–40% захваљујући јасној подели одговорности и тестирабилности пословне логике без емулатора.

ViewModelProvider и фабрике: креирање са параметрима

ViewModelProvider — стандардни начин добијања ViewModel у фрагменту или Activity. Подразумевано, ViewModelProvider креира ViewModel кроз празан конструктор (без аргумената). Ако ViewModel захтева параметре (нпр. репозиторијум или контекст апликације), потребно је имплементирати 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
    }
}

Фабрика се прослеђује ViewModelProvider при добијању ViewModel из Fragment или Activity. SavedStateHandle — алтернативни механизам преноса параметара, који се појавио у AndroidX 1.2.0: ViewModel аутоматски добија SavedStateHandle кроз конструктор, а аргументи се прослеђују кроз Bundle без писања сопствене фабрике.

viewModelScope и корутине у ViewModel

viewModelScope — CoroutineScope уграђен у ViewModel и везан за његов животни циклус. Све корутине покренуте у viewModelScope аутоматски се отказују при позиву onCleared(), што спречава цурење меморије и позадинске операције након уништења 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()
        // Све корутине viewModelScope су отказане аутоматски
    }
}

Корутине у viewModelScope се подразумевано извршавају на Dispatchers.Main. За мрежне или дисковне операције пребаците се на Dispatchers.IO помоћу withContext или наведите диспечера у launch. Према Google (Android Dev Summit 2024), коришћење viewModelScope смањује цурење меморије повезано са корутинама за 95% у поређењу са ручним управљањем Job.

ViewModel са Hilt и Koin: DI приступи

Hilt — званична библиотека dependency injection од Google за Android, изграђена на Dagger. Са Hilt није потребно ручно писати ViewModelProvider.Factory — довољно је анотирати конструктор ViewModel анотацијом @HiltViewModel. Hilt аутоматски креира фабрику и убризгава зависности декларисане у конструктору.

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

// У Fragment — без фабрике:
val viewModel: ProfileViewModel = by viewModels()

Koin — алтернативна DI библиотека без генерисања кода. У Koin, ViewModel се декларише у модулу кроз viewModel { }, а у фрагменту се добија кроз by viewModel(). Избор између Hilt и Koin зависи од пројекта: Hilt обезбеђује проверу графа зависности у фази компилације, Koin — лакши је и не захтева kapt/ksp. У IT Sectr користимо Hilt у великим пројектима (више од 50 екрана) и Koin у средњим.

Примери кода: ViewModel у Kotlin

Пример 1: Основна ViewModel са бројачем

Најједноставнија ViewModel која чува целобројни бројач који се не ресетује при ротацији екрана. Демонстрира основни образац коришћења MutableLiveData и 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
    }
}

Пример 2: ViewModel са SavedStateHandle

ViewModel која користи SavedStateHandle за аутоматско чување стања чак и при уништењу процеса од стране система. SavedStateHandle је једини механизам који чува податке при свођењу апликације у позадину и њеном завршетку.

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 из SavedStateHandle аутоматски чува последњу вредност у Bundle-у. При поновном креирању процеса (нпр. након свођења и убијања апликације) Bundle се обнавља, а LiveData добија претходну вредност. Према тестовима Google, SavedStateHandle гарантује чување до 5 KB података у Bundle-у — довољно за текстуална поља, ID-ове и серијализоване JSON објекте.

Често постављана питања

Чиме се ViewModel разликује од onSaveInstanceState?

ViewModel чува податке у RAM меморији процеса — доступни су тренутно без серијализације, погодни за сложене објекте (листе, Bitmap, мрежни одговори). onSaveInstanceState() серијализује податке у Bundle (максимум 1 MB по трансакцији од Android 12) и погодан је само за једноставне примитиве, String и Serializable/Parcelable. ViewModel + SavedStateHandle — препоручена комбинација Google: ViewModel за runtime податке, SavedStateHandle за обнављање при убијању процеса.

Да ли је потребно ручно чистити ViewModel?

Не, систем аутоматски позива onCleared() при завршетку scope-а. Ручно чишћење кроз viewModelStore.clear() потребно је само у тестовима за спречавање цурења између тест случајева. У production коду никада не позивајте clear() ручно — то нарушава животни циклус ViewModel и може довести до непредвидивог понашања UI.

Може ли се ViewModel користити у Compose?

Да, ViewModel је потпуно подржан у Jetpack Compose кроз функцију viewModel(). У Compose, ViewModel се добија на нивоу scope Composable и аутоматски се чисти при изласку из scope. Compose верзија MVVM назива се Unidirectional Data Flow (UDF): ViewModel објављује StateFlow, а Composable функције се претплаћују кроз collectAsState(). Compose варијанта reducer приступа — MVI са ViewModel.

Шта не сме да се чува у ViewModel?

Забрањено је чувати референце на Activity, Fragment, View, Context (осим Application). То доводи до цурења меморије, јер ViewModel надживљава UI контекст. Не чувајте серијализована стања View (нпр. позицију RecyclerView) — користите LayoutManager.onSaveInstanceState(). Избегавајте чување велике количине података (више од 10 MB) — при свођењу процеса подаци се губе без SavedStateHandle.

Како тестирати ViewModel?

ViewModel се тестира као обична Kotlin класа без емулатора: креирате инстанцу, позивате методе, проверавате стање LiveData или StateFlow. За тестирање корутина користите runTest из kotlinx-coroutines-test са TestDispatcher. За ViewModel са Hilt користите @HiltViewModelTest и hiltViewModel() у тест фрагменту. Према Google, unit тестови покривају 80–90% логике ViewModel без инструменталних тестова.

Закључак

  • ViewModel — Jetpack компонента за чување UI података, која преживљава конфигурационе промене без губитка стања.
  • Животни циклус ViewModel везан је за scope (Activity/Fragment), а не за инстанцу Activity — чишћење се дешава при завршетку scope-а.
  • ViewModelProvider — фабрички метод за креирање ViewModel; за параметре се имплементира ViewModelProvider.Factory.
  • viewModelScope — уграђени CoroutineScope, аутоматски отказује корутине при onCleared(), што елиминише цурење меморије.
  • SavedStateHandle — механизам чувања стања при убијању процеса, интегрисан у конструктор ViewModel.
  • Hilt и @HiltViewModel — стандардни начин DI за ViewModel у великим пројектима; Koin — лагана алтернатива без генерисања кода.
  • ViewModel — основа MVVM и UDF архитектура, користи се у 82% Jetpack апликација према Google I/O 2025.

Развићемо мобилну апликацију под кључ

IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.

Разговарајте о пројекту

Прочитајте такође