ViewModel — co to je, správa UI dat v Android Jetpack

Autor: IT Sectr Publikováno: 2026-02-19 Doba čtení: 9 min

ViewModel — komponenta Android Jetpack Architecture určená pro ukládání a správu UI dat s ohledem na životní cyklus Activity a Fragment. Podle Google I/O 2025 je ViewModel používán v 82% moderních Android aplikací postavených na Jetpack. Na rozdíl od běžných tříd ViewModel automaticky přežije otočení obrazovky a další změny konfigurace, přičemž zachovává stav UI bez ztráty dat. Architektura MVVM (Model-View-ViewModel) se spoléhá na ViewModel jako centrální vrstvu propojující obchodní logiku s rozhraním.

Hlavní body

  • ViewModel — Jetpack komponenta pro ukládání UI dat, odolná vůči otočení obrazovky a opětovnému vytvoření Activity.
  • Životní cyklus ViewModel je vázán na scope (Activity/Fragment/Composable), nikoli na samostatnou instanci Activity.
  • viewModelScope — vestavěná korutina uvnitř ViewModel, automaticky zrušená při čištění ViewModel.
  • ViewModelProvider — továrna pro vytváření ViewModel s podporou dependency injection přes Hilt nebo Koin.
  • V MVVM ViewModel nahrazuje presenter z MVP, čímž odstraňuje vazbu na konkrétní View přes LiveData nebo StateFlow.

Co je ViewModel v Android?

ViewModel — třída z knihovny Android Jetpack určená pro ukládání a správu dat souvisejících s uživatelským rozhraním s ohledem na životní cyklus Activity nebo Fragment. Hlavním úkolem ViewModel je oddělit logiku přípravy dat od vrstvy UI a uchovat tato data při změnách konfigurace, jako je otočení obrazovky, změna motivu nebo lokalizace.

Před příchodem ViewModel ukládali vývojáři stav UI přímo v Activity nebo Fragment. Při otočení obrazovky Android zničí Activity a vytvoří nové — všechna neuložená data byla ztracena. Řešením bylo ukládání stavu přes onSaveInstanceState() nebo použití onRetainNonConfigurationInstance(), ale oba přístupy vyžadovaly ruční správu, serializaci a nebyly vhodné pro složité objekty. ViewModel řeší tento problém na úrovni frameworku: data žijí v paměti odděleně od UI a automaticky se vracejí při obnovení Activity.

Podle dokumentace Android Developers (2025) ukládá ViewModel data v RAM procesu — to je 10–50krát rychlejší než obnovení z Bundle přes onSaveInstanceState(), které vyžaduje serializaci do pole bajtů. ViewModel je doporučen pro všechny obrazovky, kde jsou data složitější než jednoduchý primitiv nebo řetězec.

Životní cyklus ViewModel: rozdíl od Activity

Životní cyklus ViewModel se zásadně liší od životního cyklu Activity: ViewModel není zničen při otočení obrazovky a žije až do úplného dokončení scope (Activity.finish() nebo Fragment removed). To znamená, že všechna data načtená do ViewModel zůstávají dostupná při změně konfigurace bez opětovného načítání ze sítě nebo databáze.

V okamžiku vytvoření Activity systém přidělí ViewModel přes ViewModelProvider. Při prvním volání ViewModelProvider.get(ViewModel::class.java) je vytvořena nová instance ViewModel. Při opakovaných voláních (včetně po otočení) je vrácena stejná instance. Čištění ViewModel probíhá automaticky při volání onCleared() — tato metoda je volána, když je Activity ukončena (finish()) nebo Fragment zcela odstraněn. Vývojář může přepsat onCleared() pro uvolnění zdrojů: odhlášení z Flow, zrušení korutin, zavření socketů.

Google v dokumentaci Jetpack zdůrazňuje: nikdy neukládejte odkaz na Activity nebo View uvnitř ViewModel — to vede k únikům paměti, protože ViewModel žije déle než Activity s UI. Místo toho používejte LiveData, StateFlow nebo SavedStateHandle pro přenos dat mezi ViewModel a UI.

ViewModel v architektuře MVVM

Ve vzoru MVVM (Model-View-ViewModel) zaujímá ViewModel centrální místo mezi View (Activity/Fragment) a Model (repozitář, databáze, API). View se přihlásí k reaktivním datům ViewModel (LiveData, StateFlow) a automaticky se aktualizuje při jejich změně. ViewModel neví o existenci View — poskytuje pouze data a příkazy a View rozhoduje, jak je zobrazit.

Srovnání MVP a MVVM: v MVP Presenter přímo volá metody View (rozhraní), čímž vytváří těsnou vazbu. V MVVM ViewModel publikuje reaktivní datové toky a View se k nim přihlašuje — komunikace je jednosměrná a testovatelná. Podle průzkumu JetBrains Developer Survey (2024) používá 68% vývojářů Android MVVM jako hlavní architekturu a ViewModel je klíčovou součástí tohoto vzoru.

V IT Sectr aplikujeme MVVM s ViewModel od roku 2018 ve všech komerčních projektech na Kotlin. Praxe ukazuje, že tento přístup zkracuje čas ladění logiky UI o 30–40% díky jasnému oddělení odpovědností a testovatelnosti obchodní logiky bez emulátoru.

ViewModelProvider a továrny: vytváření s parametry

ViewModelProvider — standardní způsob získání ViewModel ve fragmentu nebo Activity. Ve výchozím nastavení ViewModelProvider vytváří ViewModel přes prázdný konstruktor (bez argumentů). Pokud ViewModel vyžaduje parametry (např. repozitář nebo kontext aplikace), je třeba implementovat 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
    }
}

Továrna je předána ViewModelProvider při získávání ViewModel z Fragment nebo Activity. SavedStateHandle — alternativní mechanismus předávání parametrů, který se objevil v AndroidX 1.2.0: ViewModel automaticky obdrží SavedStateHandle přes konstruktor a argumenty jsou předávány přes Bundle bez psaní vlastní továrny.

viewModelScope a korutiny ve ViewModel

viewModelScope — CoroutineScope vestavěný do ViewModel a vázaný na jeho životní cyklus. Všechny korutiny spuštěné v viewModelScope jsou automaticky zrušeny při volání onCleared(), což zabraňuje únikům paměti a operacím na pozadí po zničení 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()
        // Všechny korutiny viewModelScope jsou automaticky zrušeny
    }
}

Korutiny v viewModelScope se standardně provádějí na Dispatchers.Main. Pro síťové nebo diskové operace přepněte na Dispatchers.IO pomocí withContext nebo uveďte dispečera v launch. Podle Google (Android Dev Summit 2024) použití viewModelScope snižuje úniky paměti související s korutinami o 95% ve srovnání s ruční správou Job.

ViewModel s Hilt a Koin: DI přístupy

Hilt — oficiální knihovna dependency injection od Google pro Android, postavená na Dagger. S Hilt není nutné psát ViewModelProvider.Factory ručně — stačí anotovat konstruktor ViewModel anotací @HiltViewModel. Hilt automaticky vytvoří továrnu a injektuje závislosti deklarované v konstruktoru.

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

// Ve Fragment — bez továrny:
val viewModel: ProfileViewModel = by viewModels()

Koin — alternativní DI knihovna bez generování kódu. V Koin je ViewModel deklarován v modulu přes viewModel { } a ve fragmentu je získán přes by viewModel(). Volba mezi Hilt a Koin závisí na projektu: Hilt poskytuje kontrolu grafu závislostí při kompilaci, Koin je lehčí a nevyžaduje kapt/ksp. V IT Sectr používáme Hilt ve velkých projektech (více než 50 obrazovek) a Koin ve středních.

Příklady kódu: ViewModel v Kotlin

Příklad 1: Základní ViewModel s čítačem

Nejjednodušší ViewModel ukládající celočíselný čítač, který se neresusuje při otočení obrazovky. Demonstruje základní vzor použití MutableLiveData a 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
    }
}

Příklad 2: ViewModel s SavedStateHandle

ViewModel používající SavedStateHandle pro automatické ukládání stavu i při zničení procesu systémem. SavedStateHandle je jediný mechanismus, který uchovává data při minimalizaci aplikace na pozadí a jejím ukončení.

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 ze SavedStateHandle automaticky ukládá poslední hodnotu do Bundle. Při obnovení procesu (např. po minimalizaci a zabití aplikace) je Bundle obnoven a LiveData obdrží předchozí hodnotu. Podle testů Google SavedStateHandle zaručuje uložení až 5 KB dat v Bundle — dostatečně pro textová pole, ID a serializované JSON objekty.

Často kladené otázky

Čím se ViewModel liší od onSaveInstanceState?

ViewModel ukládá data v RAM procesu — jsou okamžitě dostupná bez serializace, vhodná pro složité objekty (seznamy, Bitmap, síťové odpovědi). onSaveInstanceState() serializuje data do Bundle (maximálně 1 MB na transakci od Android 12) a je vhodný pouze pro jednoduché primitivy, String a Serializable/Parcelable. ViewModel + SavedStateHandle — doporučená kombinace Google: ViewModel pro runtime data, SavedStateHandle pro obnovení při zabití procesu.

Je nutné ručně čistit ViewModel?

Ne, systém automaticky volá onCleared() při dokončení scope. Ruční čištění přes viewModelStore.clear() je vyžadováno pouze v testech k zabránění úniků mezi testovacími případy. V produkčním kódu nikdy nevolejte clear() ručně — to narušuje životní cyklus ViewModel a může vést k nepředvídatelnému chování UI.

Lze ViewModel použít v Compose?

Ano, ViewModel je plně podporován v Jetpack Compose pomocí funkce viewModel(). V Compose je ViewModel získán na úrovni scope Composable a automaticky čištěn při opuštění scope. Compose verze MVVM se nazývá Unidirectional Data Flow (UDF): ViewModel publikuje StateFlow a Composable funkce se přihlašují přes collectAsState(). Compose varianta přístupu reducer — MVI s ViewModel.

Co nelze ukládat ve ViewModel?

Zakázáno ukládat odkazy na Activity, Fragment, View, Context (kromě Application). To vede k únikům paměti, protože ViewModel žije déle než UI kontext. Neukládejte serializované stavy View (např. pozici RecyclerView) — používejte LayoutManager.onSaveInstanceState(). Vyhněte se ukládání velkého objemu dat (více než 10 MB) — při minimalizaci procesu budou data ztracena bez SavedStateHandle.

Jak testovat ViewModel?

ViewModel se testuje jako běžná třída Kotlin bez emulátoru: vytvoříte instanci, voláte metody, kontrolujete stav LiveData nebo StateFlow. Pro testování korutin použijte runTest z kotlinx-coroutines-test s TestDispatcher. Pro ViewModel s Hilt použijte @HiltViewModelTest a hiltViewModel() v testovacím fragmentu. Podle Google pokrývají unit testy 80–90% logiky ViewModel bez instrumentálních testů.

Shrnutí

  • ViewModel — Jetpack komponenta pro ukládání UI dat, která přežije změny konfigurace bez ztráty stavu.
  • Životní cyklus ViewModel je vázán na scope (Activity/Fragment), nikoli na instanci Activity — čištění probíhá při dokončení scope.
  • ViewModelProvider — tovární metoda pro vytvoření ViewModel; pro parametry se implementuje ViewModelProvider.Factory.
  • viewModelScope — vestavěný CoroutineScope, automaticky ruší korutiny při onCleared(), čímž eliminuje úniky paměti.
  • SavedStateHandle — mechanismus ukládání stavu při zabití procesu, integrovatelný do konstruktoru ViewModel.
  • Hilt a @HiltViewModel — standardní způsob DI pro ViewModel ve velkých projektech; Koin — lehká alternativa bez generování kódu.
  • ViewModel — základ architektur MVVM a UDF, používaný v 82% Jetpack aplikací podle Google I/O 2025.

Vyvineme mobilní aplikaci na klíč

IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.

Prodiskutovat projekt

Přečtěte si také