viewModelScope — bu, androidx.lifecycle kitabxanasından daxili CoroutineScope-dir. O, ViewModel-in həyat dövrünə bağlanır və təmizlənmə zamanı avtomatik olaraq ləğv edilir. Google Android Developers, 2025-ə görə, viewModelScope MVVM arxitekturasında korutinlərin işə salınması üçün standart mexanizmdir. O, yaddaş sızması riski olmadan asinxron əməliyyatlarla təhlükəsiz işləməyi təmin edir. ViewModelScope defolt olaraq Dispatchers.Main istifadə edir və onun daxilindəki bütün IO əməliyyatları withContext vasitəsilə yerinə yetirilməlidir.
Əsas məqamlar
viewModelScope — bu, lifecycle-viewmodel-ktx kitabxanasında (2.1.0 versiyasından) əlavə edilmiş ViewModel interfeysinə extension-xüsusiyyətdir. O, ViewModel-in həyat dövrünə bağlanmış hazır CoroutineScope təmin edir.
// Daxili struktur (sadələşdirilmiş)
val ViewModel.viewModelScope: CoroutineScope
get() {
val scope = this.getTag(JOB_KEY)
if (scope != null) return scope
return CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
.also { setTag(JOB_KEY, it) }
}
Scope ilk müraciətdə tənbəl (lazy) şəkildə yaradılır və setTag vasitəsilə keşlənir. SupervisorJob istifadə olunur, yəni bir uşaq korutindəki istisna qalanlarını ləğv etmir. Defolt dispatcher Dispatchers.Main.immediate-dir, o, əgər çağırış artıq Main-dədirsə, əlavə dispetçerizasiya olmadan kodu əsas axında yerinə yetirir.
ViewModel həyat dövrünü tərk etdikdə (Activity tamamlandıqda və ya Fragment silindikdə), sistem clear() çağırır, o da onCleared()-ı işə salır. Bu callback-də viewModelScope öz Job-unu ləğv edir, bu da rekursiv olaraq bütün aktiv korutinləri bitirir. Mexanizm Closeable interfeysi vasitəsilə həyata keçirilir, burada scope-un Job-ı avtomatik bağlanma üçün resurs kimi qeydiyyatdan keçirilir.
viewModelScope-un ViewModel-in həyat dövrünə bağlanma mexanizmi teqləmə və onCleared callback-inə əsaslanır. Bunun addım-addım necə işlədiyinə baxaq.
ViewModel viewModelScope.launch { ... } yerinə yetirdikdə, getter JOB_KEY teqi altında saxlanılmış scope-un olub-olmadığını yoxlayır. Scope hələ yaradılmayıbsa — CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)-nin yeni nüsxəsi yaradılır. Scope daxili teq map-i vasitəsilə ViewModel daxilində saxlanılır.
viewModelScope.launch və ya viewModelScope.async vasitəsilə işə salınmış bütün korutinlər SupervisorJob scope-na uşaq olurlar. Onlar əsas axında işləyirlər (əgər withContext vasitəsilə başqa dispatcher göstərilməyibsə). ViewModel yaşadığı müddətcə — korutinlər aktiv, dayandırılmış və ya bitmiş ola bilər.
Sistem ViewModel-i məhv etdikdə, ViewModel.clear() çağırılır. clear() daxilində aşağıdakılar baş verir:
Ekran çevrildikdə Activity yenidən yaradılır, lakin ViewModel saxlanılır (ViewModelStoreOwner sayəsində). Bu o deməkdir ki, viewModelScope aktiv qalır və korutinlər kəsintisiz işləməyə davam edir. Activity yenidən yaradıldıqdan sonra eyni ViewModel (və eyni scope) təkrar istifadə olunur — məlumat yüklənməsi yenidən başlamır.
MVVM (Model-View-ViewModel) — Google tərəfindən Android proqramları üçün tövsiyə olunan arxitekturadır. viewModelScope burada asinxron əməliyyatların icraçısı kimi mərkəzi yer tutur.
| Qat | Komponent | viewModelScope-un rolu |
|---|---|---|
| UI | Activity / Fragment | ViewModel-dən StateFlow/LiveData-nı müşahidə edir |
| ViewModel | ViewModel | viewModelScope vasitəsilə korutinləri işə salır, UI vəziyyətini idarə edir |
| Repository | Repository | viewModelScope korutinlərindən çağırılan suspend-funksiyaları qəbul edir |
| Data | DAO / Api | Faktiki sorğuları yerinə yetirir (Room, Retrofit) |
ViewModel viewModelScope vasitəsilə korutinləri işə salır, onların daxilində Repository-nin suspend-funksiyalarını çağırır. Nəticə UI qatı tərəfindən müşahidə olunan StateFlow-a çevrilir. Bu sxem məsuliyyətlərin aydın bölünməsini və hər bir qatın müstəqil sınaqdan keçirilməsini təmin edir.
Əgər korutinlər Fragment-dən işə salınsaydı, ekran çevrildikdə onlar Fragment-in məhv edilməsi ilə birlikdə ləğv olunardı. ViewModel rotasiyadan sağ çıxır, buna görə də onun scope-da işə salınmış korutinlər işləməyə davam edir. Bu, məlumat yükləmə zamanı viewModelScope-un lifecycleScope qarşısında əsas üstünlüyüdür.
Kotlin dilində Android proqramında viewModelScope-dan istifadənin üç praktik ssenarisini nəzərdən keçirək.
class ProfileViewModel(
private val repo: ProfileRepository
) : ViewModel() {
private val _profile = MutableStateFlow<Profile?>(null)
val profile: StateFlow<Profile?> = _profile
init {
loadProfile()
}
private fun loadProfile() {
viewModelScope.launch {
val result = repo.getProfile()
_profile.value = result
}
}
}
init blokunda dərhal profil yüklənməsi başlayır. Korutina defolt olaraq əsas axında işləyir. Repozitori öz suspend-funksiyası daxilində şəbəkə sorğusu üçün withContext(Dispatchers.IO) istifadə edir, buna görə ViewModel axınların dəyişdirilməsi ilə məşğul olmur.
sealed class UiState {
object Loading : UiState()
data class Success(val data: List<Item>) : UiState()
data class Error(val message: String) : UiState()
}
fun fetchItems() {
_state.value = UiState.Loading
viewModelScope.launch {
try {
val items = repo.getItems()
_state.value = UiState.Success(items)
} catch (e: Exception) {
_state.value = UiState.Error(e.message ?: "Unknown error")
}
}
}
UI vəziyyəti sealed class UiState vasitəsilə təsvir olunur. ViewModel hər dəyişiklikdə state-i yeniləyir. Fragment StateFlow-a abunə olub yalnız aktual vəziyyətə reaksiya verir, təkrar rotasiyalarda köhnəlmiş çağırışları nəzərə almır.
private var searchJob: Job? = null
fun search(query: String) {
searchJob?.cancel()
searchJob = viewModelScope.launch {
delay(300)
val results = repo.search(query)
_searchResults.value = results
}
}
Hər yeni axtarış sorğusunda əvvəlki korutina ləğv edilir. delay(300) debounce-u həyata keçirir — axtarış yalnız daxil etmədə 300 ms fasilədən sonra yerinə yetirilir. Bu, server yükünü azaldır və köhnəlmiş nəticələrin qarşısını alır.
Hər iki scope AndroidX Lifecycle kitabxanası tərəfindən təmin edilir, lakin müxtəlif həyat dövrlərinə bağlıdır. Onların arasında seçim tapşırığın növündən asılıdır.
| Xüsusiyyət | viewModelScope | lifecycleScope |
|---|---|---|
| Sahibi | ViewModel | LifecycleOwner (Activity/Fragment) |
| Rotasiyada ləğv | Xeyr (ViewModel saxlanılır) | Bəli (Activity yenidən yaradılır) |
| Defolt dispatcher | Dispatchers.Main.immediate | Dispatchers.Main.immediate |
| Harada mövcuddur | ViewModel | Activity, Fragment, Service |
| Tip ssenari | Məlumat yükləmə, biznes məntiqi | UI qarşılıqlı əlaqəsi, animasiyalar, snackbarlar |
Google məlumatların yüklənməsi və işlənməsi ilə bağlı bütün tapşırıqlar üçün viewModelScope-dan istifadə etməyi tövsiyə edir. lifecycleScope UI-nin konkret anına bağlı əməliyyatlar üçün tətbiq edilməlidir — məsələn, ekranın ilk görünməsi zamanı animasiyanın başladılması və ya ekrandan çıxışda dayandırılmalı olan Location yeniləmələrinə abunəlik.
Hətta Android-in yaxşı sənədləşdirilmiş API-lərində belə tərtibatçılar xarakterik səhvlərə yol verirlər. Dörd ən çox rast gəlinən problemi nəzərdən keçirək.
Ən məkrli səhv — ViewModel təmizləndikdən sonra StateFlow və ya LiveData-nı yeniləməyə cəhd etməkdir. viewModelScope onCleared() zamanı ləğv edilsə də, korutina faktiki ləğv anına qədər kodu yerinə yetirə bilər. Yoxlamaq üçün isActive istifadə edin və ya catch blokunun tamamlanmasına güvənin.
viewModelScope daxildə SupervisorJob istifadə edir ki, bu da səhvləri korutinlər arasında izolyasiya edir. Amma viewModelScope.launch daxilində öz Job() ilə korutina işə salsanız, həmin korutina SupervisorJob-a uşaq olacaq, lakin digər korutinlərdəki səhvlər zamanı ləğvdən qorunmayacaq.
viewModelScope-un sərt limiti olmasa da, minlərlə aktiv korutina sistemi ləngidə bilər. Uzun məlumat siyahıları üçün hər element üçün ayrıca korutina yaratmaq əvəzinə Flow ilə collectLatest istifadə edin.
Təsadüfən viewModelScope əvəzinə GlobalScope idxal etsəniz, korutina ViewModel təmizlənərkən ləğv edilməyəcək. Bu, yaddaş sızmasına və potensial crash-a səbəb olacaq. Həmişə korutinlərin viewModelScope vasitəsilə işə salındığını yoxlayın, xüsusilə varis fragmentlərdə.
Tez-tez verilən suallar
Birbaşa viewModelScope-un dispatcherini dəyişmək olmaz — o, Dispatchers.Main.immediate kimi sərt şəkildə təyin edilib. Amma korutina daxilində withContext vasitəsilə başqa dispatcherə keçmək olar. Testlərdə dispatcheri dəyişmək üçün Rule vasitəsilə TestDispatcher istifadə edin.
Scope-u Repository-ə ötürməyin — bu arxitektura prinsiplərini pozur. Repository suspend-funksiyalar təmin etməli, ViewModel isə viewModelScope vasitəsilə korutinləri özü idarə etməlidir. Əgər Repository scope tələb edirsə — arxitekturanı Clean Architecture lehinə yenidən nəzərdən keçirin.
SupervisorJob bir korutindəki istisnanın (məsələn, bir neçə müstəqil sorğudan birinin yüklənmə xətası) qalan korutinləri ləğv etməməsinə zəmanət verir. Bu, müxtəlif ekranların müstəqil məlumatlar yüklədiyi ViewModel ssenarisinə uyğundur.
Bəli, viewModelScope UI növündən (View System və ya Jetpack Compose) asılı olmayaraq istənilən ViewModel-də mövcuddur. Compose-da korutinlər də viewModelScope vasitəsilə işə salınır, UI effektləri üçün isə LaunchedEffect və rememberCoroutineScope istifadə olunur.
viewModelScope.cancel() çağırışı scope-u dərhal ləğv edir — bütün aktiv korutinlər CancellationException ilə bitir. Bundan sonra viewModelScope.launch çağırsanız, yeni scope getterə növbəti müraciətdə avtomatik yaradılacaq.
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