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 — 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 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.
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 — 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.
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 — 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.
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.
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.
@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.
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.
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
}
}
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í.
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
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.
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.
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.
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.
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í
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í.
Přečtěte si také