LaunchedEffect — bu, Jetpack Compose-da asinxron əməliyyatları komponentin həyat dövrünə bağlı korutin daxilində yerinə yetirmək üçün nəzərdə tutulmuş composable-funksiyadır. O, composable elementinin kompozisiyaya daxil olması zamanı kod blokunu işə salır və çıxışda avtomatik olaraq ləğv edir. Bu, LaunchedEffect-i məlumat yükləmə, Flow-a abunə olma və taymerlərlə iş üçün əsas alətə çevirir. Android Documentation (2025)-a görə, LaunchedEffect asinxron məlumatlarla işləyən Jetpack Compose tətbiqlərinin 85%-də istifadə olunur.
Əsas məqamlar
LaunchedEffect — bu, Jetpack Compose-da DisposableEffect, SideEffect, SnapshotEffect və rememberCoroutineScope ilə yanaşı beş side-effect API-dən biridir. Onun özəlliyi — kodu komposable elementinin həyat dövrünə bağlı asinxron korutin kontekstində icra etməsidir. Adi callback-funksiyalardan fərqli olaraq, LaunchedEffect UI-ni bloklamır və şəbəkə sorğuları və ya gecikmə gözləməsi kimi uzunmüddətli əməliyyatları yerinə yetirə bilər.
Daxili olaraq LaunchedEffect kompozisiya tərəfindən təmin edilən CoroutineScope-dan istifadə edir. Bu scope, composable elementi kompozisiyanı tərk etdikdə avtomatik ləğv edilir. Belə bir bağlama, ekran bağlandıqdan sonra heç bir korutinin işləməyə davam etməyəcəyinə zəmanət verir — bu, ViewModel və ya Application scope-dakı qlobal korutinlərdən əsas fərqdir.
Android Developers Blog (2025)-a görə, LaunchedEffect xüsusi olaraq Compose dünyasında LiveData-observer nümunəsini əvəz etmək üçün hazırlanmışdır. LiveData-ya observeAsState vasitəsilə abunə olmaq və abunəliyi ayrıca idarə etmək əvəzinə, tərtibatçılar Flow-da collectAsState ilə LaunchedEffect-dən istifadə edir ki, bu da daha proqnozlaşdırıla bilən həyat dövrü idarəetməsi təmin edir və açıq ləğv olmadan abunəliklərə xas olan yaddaş sızmalarını aradan qaldırır.
@Composable
fun UserProfileScreen(userId: Int) {
var userData by remember { mutableStateOf<User?>(null) }
LaunchedEffect(userId) {
val result = userRepository.fetchUser(userId)
userData = result
}
// userData əsasında interfeys
}
LaunchedEffect-in ən vacib mexanizmi — açar sistemidir. Funksiyanın ilk parametri — vararg keys: Any? — effektin nə vaxt yenidən işə salınmalı olduğunu müəyyən edir. LaunchedEffect əvvəlki açar dəyərlərini saxlayır və hər rekompozisiyada onları yeniləri ilə müqayisə edir. Əgər ən azı bir açar dəyişibsə (equals() vasitəsilə), cari korutin ləğv edilir və yeni korutin işə salınır.
Əgər açar, məsələn, userId-dirsə, istifadəçi identifikatoru dəyişdikdə LaunchedEffect avtomatik olaraq cari sorğunu ləğv edəcək və yenilənmiş userId ilə yenisini işə salacaq. Bu, tərtibatçını əvvəlki sorğunun əl ilə ləğv edilməsindən və məlumatların aktuallığının yoxlanılmasından azad edir — hər şey açarlar vasitəsilə deklarativ idarə olunur. Belə yanaşma Jetpack Compose-un reaktiv paradiqmasına uyğundur.
Vacib qayda: əgər açar kimi sabit ötürülərsə — LaunchedEffect(Unit) — effekt klassik Android-də onStart və ya onResume analoqu kimi kompozisiyaya daxil olduqda yalnız bir dəfə icra olunacaq. Əgər açarlar ötürülməzsə — LaunchedEffect(Unit) — effekt bir dəfə işləyəcək. Əgər boş mötərizələr ötürülərsə — LaunchedEffect kompilyasiya olunmayacaq, çünki açarlar məcburi parametrdir.
// Ekran göründükdə birdəfəlik icra
LaunchedEffect(Unit) {
analytics.logScreenView("Profile")
}
// userId dəyişdikdə yenidən başlat
LaunchedEffect(userId) {
loadUserData(userId)
}
// Çoxsaylı açarlar
LaunchedEffect(userId, filter, sortOrder) {
fetchFilteredData(userId, filter, sortOrder)
}
Hər iki API Jetpack Compose-da side effects-ə aid olsa da, LaunchedEffect və DisposableEffect prinsipial olaraq fərqli vəzifələri həll edir. LaunchedEffect açarlar üzrə yenidən işə salma imkanı ilə asinxron korutinlər üçün nəzərdə tutulub, DisposableEffect isə korutinsiz sinxron konfiqurasiya və resursların təmizlənməsi əməliyyatları üçündür.
Əsas fərq — onDispose-un DisposableEffect-də olmasıdır. LaunchedEffect-də açıq təmizləmə bloku yoxdur: korutinin ləğvi açar dəyişdikdə və ya kompozisiyadan çıxdıqda avtomatik baş verir, lakin tərtibatçı bu ləğv anında öz kodunu daxil edə bilməz. DisposableEffect isə kompozisiyadan çıxışda zəmanətli icra olunan onDispose bloku təmin edir ki, bu da yerli resursların sərbəst buraxılması üçün kritikdir.
| Xarakteristika | LaunchedEffect | DisposableEffect |
|---|---|---|
| İcra | Asinxron (korutin) | Sinxron |
| onDispose | Yox (korutinin avtoləğvi) | Var (açıq təmizləmə bloku) |
| Açarlar | Köhnə korutinin ləğvi + yenidən işə salma | onDispose icrası + təkrar inisiallaşdırma |
| Tipik tətbiq | Şəbəkə sorğuları, Flow abunəlikləri, taymerlər | BroadcastReceiver, sensorlar, yerli listener-lər |
| Çıxışda ləğv | Avtomatik | onDispose vasitəsilə |
Google məqaləsi „Compose Side Effects: Deep Dive” (2025)-a görə, LaunchedEffect və DisposableEffect arasında düzgün seçim resursun növü ilə müəyyən edilir: əgər əməliyyat ləğv imkanı olan korutindirsə — LaunchedEffect istifadə edin. Əgər resurs açıq şəkildə close(), unregister() və ya dispose() çağırışı tələb edirsə — DisposableEffect istifadə edin.
LaunchedEffect-in ən çox yayılmış istifadə ssenarisi — ekran açıldıqda məlumat yükləməkdir. Nümunə sadədir: LaunchedEffect daxilində repository və ya UseCase-in suspend-funksiyası çağırılır, nəticə state dəyişəninə təyin edilir və UI avtomatik yenidən çəkilir. LaunchedEffect zəmanət verir ki, ekran yenidən açıldıqda (məsələn, geri qayıtdıqda) açarlar dəyişibsə, yükləmə təkrar icra olunur.
Yükləmə vəziyyətlərini göstərmək üçün üçlük vəziyyətlərdən istifadə olunur: Loading, Success, Error. LaunchedEffect try-catch ilə əhatə olunur və uğur halında state = Success(data), səhv halında isə state = Error(exception) təyin edilir. UI state-ə reaksiya verir və müvafiq ekranı göstərir: shimmer-yükləyici, məlumatlar və ya təkrar düyməsi olan səhv ekranı.
Əgər sürüşdürərkən məlumat yükləmə tələb olunursa (pagination), LaunchedEffect LazyColumn və LazyListState ilə birləşdirilir: siyahının sonuna çatdıqda LaunchedEffect açarı yenilənir (məsələn, səhifə sayğacı), bu da növbəti məlumat hissəsinin yüklənməsini tetikleyir.
@Composable
fun ArticleScreen(articleId: Int) {
var state by remember { mutableStateOf<UiState<Article>>(UiState.Loading) }
LaunchedEffect(articleId) {
state = UiState.Loading
state = try {
UiState.Success(articleRepository.fetch(articleId))
} catch (e: Exception) {
UiState.Error(e)
}
}
when (val s = state) {
is UiState.Loading -> ShimmerPlaceholder()
is UiState.Success -> ArticleContent(s.data)
is UiState.Error -> ErrorScreen(s.error)
{ // onRetry callback (state updates) }
}
}
LaunchedEffect açarlarından düzgün istifadə — effektlərlə səmərəli işin açarıdır (burada söz oyunu mahiyyəti əks etdirir). Əgər açar tez-tez dəyişən mutable dəyərdirsə (məsələn, hər simvol daxil edilməsində axtarış sorğusunun mətni), hər simvol əvvəlki korutini ləğv edəcək və yenisini işə salacaq. Gecikmə ilə axtarış üçün bu lazımsızdır — korutinin daxilində debounce istifadə etmək daha yaxşıdır.
Debounce-u LaunchedEffect daxilində tətbiq etmək üçün əsas hərəkəti yerinə yetirməzdən əvvəl delay() istifadə edin. Məsələn, axtarış zamanı: LaunchedEffect(query) hər query dəyişikliyində işə salınır, lakin sorğunu icra etməzdən əvvəl delay(500) dayanır. Əgər istifadəçi 500 ms keçməmiş növbəti simvolu daxil edərsə, korutin ləğv edilir (açar dəyişikliyinə görə) və yenisi işə salınır — beləliklə, sorğu yalnız daxil etmədə 500 ms fasilədən sonra göndərilir.
Digər bir texnika — açar kimi sealed class istifadə etmək. Bu, effektin nə vaxt yenidən işə salınmalı olduğunu dəqiq idarə etməyə imkan verir. Məsələn, açar-sarğıcı identifikator və məcburi yeniləmə bayrağı ehtiva edir: bayraq false-dan true-ya dəyişdikdə LaunchedEffect identifikator dəyişməsə belə yenidən işə salınır. Belə bir nümunə pull-to-refresh üçün əlverişlidir.
// Debounce 500ms ilə axtarış
LaunchedEffect(searchQuery) {
delay(500)
searchResults.value = repository.search(searchQuery)
}
// Məcburi yeniləmə ilə çəkmə-yeniləmə
data class RefreshKey(val id: Int, val refreshTrigger: Int)
var refreshTrigger by remember { mutableIntStateOf(0) }
LaunchedEffect(RefreshKey(userId, refreshTrigger)) {
articles = repository.loadUserArticles(userId)
}
Birinci və ən geniş yayılmış səhv — LaunchedEffect-in açarsız istifadəsi. Əgər LaunchedEffect { ... } arqumentsiz yazılsa, korutin hər rekompozisiyada yenidən işə salınacaq və bu, sonsuz sorğu dövrünə gətirib çıxaracaq. LaunchedEffect ən azı bir açar tələb edir — adətən birdəfəlik icra üçün Unit.
İkinci səhv — LaunchedEffect-i collect olmadan Flow-a abunə olmaq üçün istifadə etmək cəhdi. Əgər LaunchedEffect daxilində Flow-da collect çağırılırsa, korutin Flow-un tamamlanmasını gözləyərək dayanacaq (StateFlow zamanı bu heç vaxt baş vermir) və onDispose bloku normal şəkildə tamamlana bilməyəcək. Düzgün yanaşma — collectLatest istifadə etməkdir, o, yeni dəyər gəldikdə əvvəlki kolleksiyanı ləğv edir.
Üçüncü səhv — iç-içə obyektlərin açar kimi ötürülməsi. Əgər açar dəyişən sahələri olan (var) data class-dırsa, LaunchedEffect dəyişikliyi tanımay bilər, çünki Compose müqayisə üçün equals() istifadə edir və o, var-sahələr üçün proqnozlaşdırılmaz işləyə bilər. Həmişə LaunchedEffect açarları kimi immutable obyektlərdən (val) və ya primitivlərdən istifadə edin.
Tez-tez verilən suallar
Açarlar ötürülməsə, LaunchedEffect kompilyasiya olunmayacaq — Kotlin vararg parametrləri üçün ən azı bir arqument tələb edir. Kompozisiyaya daxil olduqda birdəfəlik icra üçün LaunchedEffect(Unit) istifadə edin və ya dəyişdikdə effektin yenidən işə salınmalı olduğu konkret dəyərləri ötürün.
Xeyr, LaunchedEffect composable kompozisiyadan çıxdıqda korutini avtomatik ləğv edir ki, bu da yaddaş sızmalarının qarşısını alır. Bununla belə, LaunchedEffect daxilindəki korutin bağlanma vasitəsilə Activity və ya Context-ə istinad saxlayırsa, sızma mümkündür — ViewModel-də uzunömürlü əməliyyatlar üçün viewModelScope istifadə edin.
LaunchedEffect kompozisiyaya daxil olduqda korutini avtomatik icra edir, açarlara bağlıdır. rememberCoroutineScope korutinləri əl ilə işə salmaq üçün scope təqdim edir, məsələn, onItemClick-ə cavab olaraq. Avtomatik side effects üçün LaunchedEffect, istifadəçi hadisələri ilə korutinləri işə salmaq üçün rememberCoroutineScope istifadə edin.
Əgər LaunchedEffect açarı qeyri-sabit tipdirsə (məsələn, var və ya equals() olmayan sinif), Compose dəyərin dəyişmədiyini tanımayaraq effekti hər rekompozisiyada yenidən işə sala bilər. Həll yolu: sabit tiplərdən (primitivlər, string-lər, val sahələri olan data class) istifadə edin və ya mutable dəyərləri remember-ə sarın.
LaunchedEffect-i kənardan dayandırmaq üçün birbaşa üsul yoxdur — idarəetmə açarlar vasitəsilə aparılır. Cari korutini ləğv etmək üçün açarı dəyişdirin. Korutinin həyat dövrünə tam nəzarət lazımdırsa, rememberCoroutineScope-dan Job ilə istifadə edin və hadisə və ya vəziyyət dəyişikliyi zamanı əl ilə job.cancel() çağırın.
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