ViewModel — Activity və Fragment-in həyat dövrü nəzərə alınmaqla UI məlumatlarını saxlamaq və idarə etmək üçün nəzərdə tutulmuş Android Jetpack Architecture komponenti. Google I/O 2025 məlumatına görə, ViewModel Jetpack üzərində qurulmuş müasir Android tətbiqlərinin 82%-də istifadə olunur. Adi siniflərdən fərqli olaraq, ViewModel avtomatik olaraq ekranın döndərilməsi və digər konfiqurasiya dəyişikliklərindən sağ çıxır, UI vəziyyətini məlumat itkisi olmadan qoruyur. MVVM (Model-View-ViewModel) arxitekturası biznes məntiqini interfeyslə birləşdirən mərkəzi təbəqə kimi ViewModel-ə əsaslanır.
Əsas məqamlar
ViewModel — Activity və ya Fragment-in həyat dövrü nəzərə alınmaqla istifadəçi interfeysi ilə əlaqəli məlumatları saxlamaq və idarə etmək üçün Android Jetpack kitabxanasından olan sinifdir. ViewModel-in əsas vəzifəsi məlumat hazırlığı məntiqini UI təbəqəsindən ayırmaq və bu məlumatları ekran döndərmə, mövzu və ya lokal dəyişikliyi kimi konfiqurasiya dəyişikliklərində qorumaqdır.
ViewModel ortaya çıxmazdan əvvəl tərtibatçılar UI vəziyyətini birbaşa Activity və ya Fragment-də saxlayırdılar. Ekran döndərildikdə Android Activity-ni məhv edir və yenisini yaradırdı — bütün saxlanılmamış məlumatlar itirdi. Həll yolu onSaveInstanceState() vasitəsilə vəziyyəti saxlamaq və ya onRetainNonConfigurationInstance() istifadə etmək idi, lakin hər iki yanaşma əl ilə idarəetmə, serializasiya tələb edirdi və mürəkkəb obyektlər üçün uyğun deyildi. ViewModel bu problemi framework səviyyəsində həll edir: məlumatlar UI-dan ayrı olaraq yaddaşda yaşayır və Activity yenidən yaradıldıqda avtomatik qayıdır.
Android Developers (2025) sənədləşməsinə görə, ViewModel məlumatları prosesin operativ yaddaşında saxlayır — bu, serializasiya tələb edən onSaveInstanceState() vasitəsilə Bundle-dən bərpadan 10–50 dəfə sürətlidir. ViewModel sadə primitiv və ya sətirdən daha mürəkkəb məlumatları olan bütün ekranlar üçün tövsiyə olunur.
ViewModel-in həyat dövrü Activity-nin həyat dövründən prinsipial olaraq fərqlənir: ViewModel ekran döndərildikdə məhv edilmir və scope-un tamamilə bitməsinə qədər (Activity.finish() və ya Fragment removed) yaşayır. Bu o deməkdir ki, ViewModel-ə yüklənmiş istənilən məlumat konfiqurasiya dəyişikliyində şəbəkədən və ya verilənlər bazasından yenidən yükləmədən əlçatan qalır.
Activity yaradıldığı anda sistem ViewModelProvider vasitəsilə ViewModel ayırır. ViewModelProvider.get(ViewModel::class.java) ilk çağırışda yeni ViewModel nümunəsi yaradılır. Təkrar çağırışlarda (o cümlədən dönmədən sonra) eyni nümunə qaytarılır. ViewModel-in təmizlənməsi onCleared() çağırıldıqda avtomatik baş verir — bu metod Activity finişləndikdə (finish()) və ya Fragment tam silindikdə çağırılır. Tərtibatçı resursları azad etmək üçün onCleared()-ı ləğv edə bilər: Flow-dan abunəni ləğv etmək, korutinləri dayandırmaq, soketləri bağlamaq.
Google Jetpack sənədləşməsində vurğulayır: ViewModel daxilində Activity və ya View-ə istinadı heç vaxt saxlamayın — bu, yaddaş sızmasına gətirib çıxarır, çünki ViewModel UI ilə Activity-dən daha uzun yaşayır. Bunun əvəzinə ViewModel və UI arasında məlumat ötürmək üçün LiveData, StateFlow və ya SavedStateHandle istifadə edin.
MVVM (Model-View-ViewModel) nümunəsində ViewModel View (Activity/Fragment) və Model (repozitori, verilənlər bazası, API) arasında mərkəzi yer tutur. View ViewModel-in reaktiv məlumatlarına (LiveData, StateFlow) abunə olur və onlar dəyişdikdə avtomatik yenilənir. ViewModel View-in mövcudluğundan xəbərsizdir — o, yalnız məlumat və əmrlər təqdim edir, View isə onları necə göstərəcəyinə qərar verir.
MVP və MVVM müqayisəsi: MVP-də Presenter birbaşa View (interfeys) metodlarını çağırır, sıx əlaqə yaradır. MVVM-də ViewModel reaktiv məlumat axınları yayımlayır, View isə onlara abunə olur — əlaqə birtərəfli və test edilə biləndir. JetBrains Developer Survey (2024) sorğusuna görə, Android tərtibatçılarının 68%-i MVVM-dən əsas arxitektura kimi istifadə edir və ViewModel bu nümunənin əsas komponentidir.
IT Sectr-də 2018-ci ildən Kotlin-dəki bütün kommersiya layihələrində MVVM-ni ViewModel ilə tətbiq edirik. Təcrübə göstərir ki, bu yanaşma məsuliyyətlərin aydın bölünməsi və biznes məntiqinin emulyator olmadan test edilə bilməsi sayəsində UI məntiqinin sazlanması vaxtını 30–40% azaldır.
ViewModelProvider — Fragment və ya Activity-də ViewModel əldə etməyin standart üsulu. Susmaya görə, ViewModelProvider ViewModel-i boş konstruktor vasitəsilə (arqumentsiz) yaradır. ViewModel parametrlər tələb edirsə (məsələn, repozitori və ya tətbiq konteksti), ViewModelProvider.Factory tətbiq etmək lazımdır.
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
}
}
Fabrik Fragment və ya Activity-dən ViewModel əldə edərkən ViewModelProvider-a ötürülür. SavedStateHandle — AndroidX 1.2.0-da ortaya çıxmış alternativ parametr ötürmə mexanizmi: ViewModel avtomatik olaraq konstruktor vasitəsilə SavedStateHandle alır və arqumentlər öz fabrikinizi yazmadan Bundle vasitəsilə ötürülür.
viewModelScope — ViewModel-ə quraşdırılmış və onun həyat dövrünə bağlı CoroutineScope-dur. viewModelScope-da işə salınmış bütün korutinlər onCleared() çağırıldıqda avtomatik ləğv edilir ki, bu da ViewModel məhv edildikdən sonra yaddaş sızmalarının və fon əməliyyatlarının qarşısını alır.
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-un bütün korutinləri avtomatik ləğv edilir
}
}
viewModelScope-dəki korutinlər susmaya görə Dispatchers.Main-də icra olunur. Şəbəkə və ya disk əməliyyatları üçün withContext istifadə edərək Dispatchers.IO-ya keçin və ya launch-da dispetçer təyin edin. Google-ə görə (Android Dev Summit 2024), viewModelScope istifadəsi korutinlərlə bağlı yaddaş sızmalarını əl ilə Job idarəsi ilə müqayisədə 95% azaldır.
Hilt — Dagger üzərində qurulmuş Android üçün Google-dan rəsmi dependency injection kitabxanası. Hilt ilə ViewModelProvider.Factory əl ilə yazmaq lazım deyil — ViewModel konstruktorunu @HiltViewModel annotasiyası ilə qeyd etmək kifayətdir. Hilt avtomatik fabrik yaradır və konstruktorda bəyan edilmiş asılılıqları injection edir.
@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-də — fabrik olmadan:
val viewModel: ProfileViewModel = by viewModels()
Koin — kod generasiyası olmayan alternativ DI kitabxanası. Koin-də ViewModel modulda viewModel { } vasitəsilə bəyan edilir, fragmentdə isə by viewModel() ilə əldə edilir. Hilt və Koin arasında seçim layihədən asılıdır: Hilt kompilyasiya mərhələsində asılılıq qrafının yoxlanmasını təmin edir, Koin isə daha yüngüldür və kapt/ksp tələb etmir. IT Sectr-də böyük layihələrdə (50 ekrandan çox) Hilt, orta layihələrdə isə Koin istifadə edirik.
Ekran döndərildikdə sıfırlanmayan tam sayğac saxlayan ən sadə ViewModel. MutableLiveData və LiveData-dan istifadənin əsas nümunəsini göstərir.
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
}
}
Proses sistem tərəfindən məhv edildikdə belə avtomatik vəziyyət saxlamaq üçün SavedStateHandle istifadə edən ViewModel. SavedStateHandle tətbiq fonda minimuma endirildikdə və dayandırıldıqda məlumatları qoruyan yeganə mexanizmdir.
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-dan avtomatik olaraq son dəyəri Bundle-də saxlayır. Proses yenidən yaradıldıqda (məsələn, minimuma endirmə və tətbiqin öldürülməsindən sonra) Bundle bərpa olunur və LiveData əvvəlki dəyəri alır. Google testlərinə görə, SavedStateHandle Bundle-də 5 KB-a qədər məlumatın saxlanmasını təmin edir — bu, mətn sahələri, ID-lər və serializə edilmiş JSON obyektləri üçün kifayətdir.
Tez-tez verilən suallar
ViewModel məlumatları prosesin operativ yaddaşında saxlayır — onlar serializasiya olmadan dərhal əlçatandır, mürəkkəb obyektlər (siyahılar, Bitmap, şəbəkə cavabları) üçün uyğundur. onSaveInstanceState() məlumatları Bundle-də serializasiya edir (Android 12-dən başlayaraq hər əməliyyat üçün maksimum 1 MB) və yalnız sadə primitivlər, String və Serializable/Parcelable üçün uyğundur. ViewModel + SavedStateHandle — Google tərəfindən tövsiyə olunan kombinasiya: ViewModel runtime məlumatları üçün, SavedStateHandle proses öldürüldükdə bərpa üçün.
Xeyr, sistem scope bitdikdə avtomatik olaraq onCleared() çağırır. viewModelStore.clear() vasitəsilə əl ilə təmizləmə yalnız testlər arasında sızmaların qarşısını almaq üçün testlərdə tələb olunur. Production kodunda heç vaxt clear()-ı əl ilə çağırmayın — bu, ViewModel-in həyat dövrünü pozur və gözlənilməz UI davranışına səbəb ola bilər.
Bəli, ViewModel Jetpack Compose-da viewModel() funksiyası vasitəsilə tam dəstəklənir. Compose-da ViewModel Composable scope səviyyəsində əldə edilir və scope-dan çıxıldıqda avtomatik təmizlənir. MVVM-in Compose versiyası Unidirectional Data Flow (UDF) adlanır: ViewModel StateFlow yayımlayır, Composable funksiyalar isə collectAsState() vasitəsilə abunə olur. Reducer yanaşmasının Compose variantı — ViewModel ilə MVI.
Qadağandır Activity, Fragment, View, Context (Application istisna olmaqla) istinadlarını saxlamaq. Bu, yaddaş sızmasına gətirib çıxarır, çünki ViewModel UI kontekstindən daha uzun yaşayır. Saxlamayın View-in serializə edilmiş vəziyyətlərini (məsələn, RecyclerView mövqeyi) — LayoutManager.onSaveInstanceState() istifadə edin. Çəkinin böyük həcmdə məlumat saxlamaqdan (10 MB-dan çox) — proses minimuma endirildikdə məlumatlar SavedStateHandle olmadan itiriləcək.
ViewModel emulyator olmadan adi Kotlin sinfi kimi test edilir: nümunə yaradırsınız, metodları çağırırsınız, LiveData və ya StateFlow vəziyyətini yoxlayırsınız. Korutinləri test etmək üçün kotlinx-coroutines-test-dən runTest ilə TestDispatcher istifadə edin. Hilt ilə ViewModel üçün @HiltViewModelTest və test fragmentində hiltViewModel() istifadə edin. Google məlumatına görə, vahid testlər instrumental testlər olmadan ViewModel məntiqinin 80–90%-ni əhatə edir.
Nəticə
Açar təslim mobil tətbiq hazırlayacağıq
IT Sectr 2017-ci ildən startaplar və bizneslər üçün iOS və Android tətbiqləri yaradır. Sizə məsləhət verəcəyik və ən yaxşı həlli təklif edəcəyik.
Həm də oxuyun