LaunchedEffect, Jetpack Compose’da bileşenin yaşam döngüsüne bağlı bir coroutine içinde asenkron işlemler gerçekleştirmek için tasarlanmış bir composable işlevidir. Composable öğe kompozisyona girdiğinde bir kod bloğunu başlatır ve çıktığında otomatik olarak iptal eder. Bu, LaunchedEffect’i veri yükleme, Flow’a abone olma ve zamanlayıcılarla çalışma için birincil araç haline getirir. Android Documentation (2025)’a göre, LaunchedEffect asenkron verilerle çalışan Jetpack Compose uygulamalarının %85’inde kullanılır.
Önemli Noktalar
LaunchedEffect, DisposableEffect, SideEffect, Effect ve rememberCoroutineScope ile birlikte Jetpack Compose’daki beş yan etki API’sinden biridir. Özelliği, kodu composable öğenin yaşam döngüsüne bağlı asenkron bir coroutine bağlamında yürütmesidir. Normal geri çağırma işlevlerinin aksine, LaunchedEffect kullanıcı arayüzünü engellemez ve ağ istekleri veya gecikme bekleme gibi uzun süreli işlemleri gerçekleştirebilir.
Perde arkasında, LaunchedEffect kompozisyon tarafından sağlanan bir CoroutineScope kullanır. Composable öğe kompozisyondan ayrıldığında bu kapsam otomatik olarak iptal edilir. Bu bağlama, ekran kapatıldıktan sonra hiçbir coroutine’in çalışmaya devam etmemesini garanti eder — bu, ViewModel veya Application kapsamındaki genel coroutine’lerden önemli bir farktır.
Android Developers Blog (2025)’a göre, LaunchedEffect özellikle Compose dünyasında LiveData-gözlemci modelini değiştirmek için tasarlanmıştır. observeAsState aracılığıyla LiveData’ya abone olmak ve aboneliği ayrı ayrı yönetmek yerine, geliştiriciler Flow üzerinde collectAsState ile LaunchedEffect kullanır; bu, daha öngörülebilir yaşam döngüsü yönetimi sağlar ve açık iptal olmadan aboneliklerin doğasında bulunan bellek sızıntılarını ortadan kaldırır.
@Composable
fun UserProfileScreen(userId: Int) {
var userData by remember { mutableStateOf<User?>(null) }
LaunchedEffect(userId) {
val result = userRepository.fetchUser(userId)
userData = result
}
// userData tabanlı kullanıcı arayüzü
}
LaunchedEffect’in en önemli mekanizması anahtar sistemidir. İşlevin ilk parametresi — vararg keys: Any? — efektin ne zaman yeniden başlatılması gerektiğini belirler. LaunchedEffect önceki anahtar değerlerini saklar ve her yeniden oluşturmada bunları yenileriyle karşılaştırır. En az bir anahtar değiştiyse (equals() aracılığıyla), geçerli coroutine iptal edilir ve yenisi başlatılır.
Anahtar örneğin userId ise, kullanıcı tanımlayıcısı değiştiğinde LaunchedEffect otomatik olarak geçerli isteği iptal eder ve güncellenmiş userId ile yeni bir istek başlatır. Bu, geliştiricinin önceki isteği manuel olarak iptal etme ve veri uygunluğunu kontrol etme zahmetinden kurtarır — her şey anahtarlar aracılığıyla bildirimsel olarak yönetilir. Bu yaklaşım, Jetpack Compose’un reaktif paradigmasıyla uyumludur.
Önemli kural: Bir sabiti anahtar olarak iletirseniz — LaunchedEffect(Unit) — efekt kompozisyona girerken yalnızca bir kez çalışır, klasik Android’deki onStart veya onResume’a benzer. Anahtar iletmezseniz, efekt kompozisyonda bir kez çalışır. Boş parantez iletirseniz, LaunchedEffect derlenmez çünkü anahtarlar zorunlu bir parametredir.
// Ekran göründüğünde tek seferlik yürütme
LaunchedEffect(Unit) {
analytics.logScreenView("Profile")
}
// userId değiştiğinde yeniden başlat
LaunchedEffect(userId) {
loadUserData(userId)
}
// Birden çok anahtar
LaunchedEffect(userId, filter, sortOrder) {
fetchFilteredData(userId, filter, sortOrder)
}
Her iki API de Jetpack Compose’daki yan etkilere ait olmasına rağmen, LaunchedEffect ve DisposableEffect temel olarak farklı görevleri çözer. LaunchedEffect, anahtarlarla yeniden başlatma özelliğine sahip asenkron coroutine’ler için tasarlanmışken, DisposableEffect coroutine olmadan senkron kurulum ve temizlik işlemleri içindir.
Temel fark, DisposableEffect’te onDispose’un bulunmasıdır. LaunchedEffect’te açık bir temizlik bloğu yoktur: coroutine iptali, anahtar değiştiğinde veya kompozisyondan çıkıldığında otomatik olarak gerçekleşir, ancak geliştirici iptal anında özel kod ekleyemez. DisposableEffect ise, kompozisyondan çıkarken garantili olarak yürütülen bir onDispose bloğu sağlar; bu, yerel kaynakları serbest bırakmak için kritiktir.
| Özellik | LaunchedEffect | DisposableEffect |
|---|---|---|
| Yürütme | Asenkron (coroutine) | Senkron |
| onDispose | Hayır (otomatik iptal) | Evet (açık temizlik bloğu) |
| Anahtarlar | Yeniden başlat + eski coroutine’i iptal et | onDispose’u çalıştır + yeniden başlat |
| Tipik kullanım | Ağ istekleri, Flow abonelikleri, zamanlayıcılar | BroadcastReceiver, sensörler, yerel dinleyiciler |
| Çıkışta iptal | Otomatik | onDispose aracılığıyla |
Google’ın “Compose Side Effects: Deep Dive” (2025) makalesine göre, LaunchedEffect ve DisposableEffect arasındaki doğru seçim kaynak türüne göre belirlenir: işlem iptal edilebilir bir coroutine ise — LaunchedEffect kullanın. Kaynak, close(), unregister() veya dispose() için açık bir çağrı gerektiriyorsa — DisposableEffect kullanın.
LaunchedEffect’in en yaygın kullanım durumu, bir ekran açıldığında veri yüklemektir. Model basittir: LaunchedEffect içinde, deponun veya UseCase’in bir askıya alma işlevi çağrılır, sonuç bir durum değişkenine atanır ve kullanıcı arayüzü otomatik olarak yeniden çizilir. LaunchedEffect, ekran yeniden açıldığında (örneğin, geri gezinme sırasında) anahtarlar değiştiyse yüklemenin yeniden yapılmasını garanti eder.
Yükleme durumlarını görüntülemek için üç durumlu bir model kullanılır: Loading, Success, Error. LaunchedEffect try-catch içine sarılır ve başarı durumunda state = Success(data), hata durumunda — state = Error(exception) ayarlanır. Kullanıcı arayüzü duruma tepki verir ve ilgili ekranı görüntüler: shimmer yükleyici, veri veya yeniden dene düğmesiyle hata ekranı.
Kaydırma sırasında veri yüklenmesi gerekiyorsa (sayfalama), LaunchedEffect LazyColumn ve LazyListState ile birleştirilir: listenin sonuna ulaşıldığında, LaunchedEffect anahtarı güncellenir (örneğin, bir sayfa sayacı) ve bu da verinin sonraki bölümünün yüklenmesini tetikler.
@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 anahtarlarının doğru kullanımı, efektlerle etkili bir şekilde çalışmanın anahtarıdır. Anahtar sık sık değişen değişebilir bir değerse (örneğin, her karakter girişinde arama sorgusu metni), her karakter önceki coroutine’i iptal eder ve yenisini başlatır. Debounce arama için bu aşırıdır — coroutine’in içinde debounce kullanmak daha iyidir.
LaunchedEffect içinde debounce uygulamak için, ana eylemi yürütmeden önce delay() kullanın. Örneğin, arama yaparken: LaunchedEffect(query) her sorgu değişikliğinde başlatılır, ancak isteği yürütmeden önce delay(500) vardır. Kullanıcı 500 ms geçmeden sonraki karakteri yazarsa, coroutine iptal edilir (anahtar değişikliği nedeniyle) ve yenisi başlatılır — böylece istek yalnızca girişte 500 ms’lik bir duraklamadan sonra gönderilir.
Başka bir teknik, anahtar olarak sealed class kullanmaktır. Bu, efektin ne zaman yeniden başlatılması gerektiği üzerinde hassas kontrol sağlar. Örneğin, bir sarmalayıcı anahtar bir tanımlayıcı ve zorunlu güncelleme bayrağı içerir: bayrak false’tan true’ya değiştiğinde, tanımlayıcı değişmemiş olsa bile LaunchedEffect yeniden başlatılır. Bu model, pull-to-refresh için kullanışlıdır.
// 500ms debounce ile arama
LaunchedEffect(searchQuery) {
delay(500)
searchResults.value = repository.search(searchQuery)
}
// Zorunlu güncellemeyle pull-to-refresh
data class RefreshKey(val id: Int, val refreshTrigger: Int)
var refreshTrigger by remember { mutableIntStateOf(0) }
LaunchedEffect(RefreshKey(userId, refreshTrigger)) {
articles = repository.loadUserArticles(userId)
}
İlk ve en yaygın hata, LaunchedEffect’i anahtarsız kullanmaktır. Bağımsız değişkenler olmadan LaunchedEffect { ... } yazarsanız, coroutine her yeniden oluşturmada yeniden başlatılır ve sonsuz bir istek döngüsüne yol açar. LaunchedEffect en az bir anahtar gerektirir — genellikle tek seferlik yürütme için Unit.
İkinci hata, Flow aboneliği için LaunchedEffect’i collect olmadan kullanmaya çalışmaktır. LaunchedEffect içinde bir Flow üzerinde collect çağırırsanız, coroutine Flow tamamlanana kadar askıya alınır (StateFlow durumunda asla gerçekleşmez). Doğru yaklaşım, yeni bir değer geldiğinde önceki toplamayı iptal eden collectLatest’i kullanmaktır.
Üçüncü hata, anahtar olarak iç içe nesneleri iletmektir. Anahtar değişebilir alanlara (var) sahip bir data class ise, LaunchedEffect değişikliği tanımayabilir çünkü Compose karşılaştırma için equals() kullanır ve bu, var alanlarıyla öngörülemez şekilde davranabilir. LaunchedEffect anahtarları olarak her zaman değişmez nesneler (val) veya ilkeller kullanın.
Sıkça Sorulan Sorular
Anahtar iletilmezse, LaunchedEffect derlenmez — Kotlin, vararg parametreleri için en az bir bağımsız değişken gerektirir. Kompozisyona girerken tek seferlik yürütme için LaunchedEffect(Unit) kullanın veya değiştiğinde efektin yeniden başlatılması gereken belirli değerleri iletin.
Hayır, LaunchedEffect composable kompozisyondan ayrıldığında coroutine’i otomatik olarak iptal ederek bellek sızıntılarını önler. Ancak, LaunchedEffect içindeki coroutine bir kapatma yoluyla bir Activity veya Context referansı tutarsa, sızıntı mümkündür — ViewModel’de uzun süreli işlemler için viewModelScope kullanın.
LaunchedEffect, anahtar bağlamasıyla kompozisyona girerken otomatik olarak bir coroutine yürütür. rememberCoroutineScope, manuel coroutine başlatma için bir kapsam sağlar (örneğin, onItemClick’e yanıt olarak). Otomatik yan etkiler için LaunchedEffect’i ve kullanıcı olaylarına dayalı coroutine başlatma için rememberCoroutineScope’u kullanın.
LaunchedEffect anahtarı kararsız bir türse (örneğin, var veya equals() olmayan bir sınıf), Compose değerin değişmediğini tanımayabilir ve her yeniden oluşturmada efekti yeniden başlatır. Çözüm: kararlı türler (ilkeller, dizeler, val alanlı data class) kullanın veya değişebilir değerleri remember içine sarın.
Dışarıdan LaunchedEffect’i durdurmanın doğrudan bir yolu yoktur — kontrol anahtarlar aracılığıyla yönetilir. Geçerli coroutine’i iptal etmek için anahtarı değiştirin. Coroutine yaşam döngüsü üzerinde tam kontrole ihtiyacınız varsa, Job ile rememberCoroutineScope kullanın ve bir olay veya durum değişikliğinde manuel olarak job.cancel() çağrısı yapın.
Özet
Anahtar teslim bir mobil uygulama geliştireceğiz
IT Sectr, 2017'den beri girişimler ve işletmeler için iOS ve Android uygulamaları oluşturmaktadır. Size danışmanlık yapacak ve en iyi çözümü önereceğiz.
Ayrıca okuyun