LaunchedEffect: mi ez, korutinok és kezelés a Jetpack Compose-ban

Szerző: IT Sectr Megjelenés: 2026-06-30 Olvasási idő: 9 perc

LaunchedEffect — egy composable-függvény a Jetpack Compose-ban, amely aszinkron műveletek végrehajtására szolgál egy korutinon belül, amely a komponens életciklusához van kötve. Elindít egy kódblokkot a composable-elem kompozícióba lépésekor, és automatikusan megszakítja azt a kilépéskor. Ez teszi a LaunchedEffect-et a fő eszközzé az adatok betöltéséhez, a Flow-ra való feliratkozáshoz és az időzítőkkel való munkához. A Android Documentation (2025) szerint a LaunchedEffect az aszinkron adatokkal dolgozó Jetpack Compose alkalmazások 85%-ban használatos.

Főbb pontok

  • LaunchedEffect — side-effect API korutinok indításához a kompozíció kontextusában.
  • Kulcsok — a kulcsok változásakor a korutin megszakításra kerül és újra indul az új értékekkel.
  • Automatikus megszakítás — a korutin automatikusan megszakad, amikor a component elhagyja a kompozíciót.
  • Aszinkronitás — a blokk a CoroutineScope-ban fut a Dispatchers.Main diszpácserrel.
  • Adatok betöltése — tipikus forgatókönyv: betöltés a hálózatról a képernyő első megjelenésekor.

Mi az a LaunchedEffect a Jetpack Compose-ban

LaunchedEffect — az öt side-effect API egyike a Jetpack Compose-ban, a DisposableEffect, SideEffect, SnapshotEffect és rememberCoroutineScope mellett. Különlegessége, hogy a kódot a composable-elem életciklusához kötött aszinkron korutin kontextusában hajtja végre. A szokásos callback-függvényekkel ellentétben a LaunchedEffect nem blokkolja a UI-t, és hosszan tartó műveleteket végezhet, mint például hálózati kérések vagy késleltetésre várás.

A motorháztető alatt a LaunchedEffect a kompozíció által biztosított CoroutineScope-ot használja. Ez a scope automatikusan megszakad, amikor a composable-elem elhagyja a kompozíciót. Egy ilyen kötés garantálja, hogy egyetlen korutin sem folytatódik a képernyő bezárása után — ez a fő különbség a ViewModel vagy Application scope globális korutinjaihoz képest.

Az Android Developers Blog (2025) szerint a LaunchedEffect kifejezetten a LiveData-observer minta helyettesítésére lett tervezve a Compose világában. Ahelyett, hogy a LiveData-ra observeAsState-n keresztül fizetnének fel és külön kezelnek a feliratkozást, a fejlesztők a LaunchedEffect-et használják a collectAsState-szel a Flow-n, ami kiszámíthatóbb életciklus-kezelést biztosít és kiküszöböli a kifejezett megszakítás nélküli feliratkozásokra jellemző memóriaszivárgásokat.

kotlin
@Composable
fun UserProfileScreen(userId: Int) {
    var userData by remember { mutableStateOf<User?>(null) }
    
    LaunchedEffect(userId) {
        val result = userRepository.fetchUser(userId)
        userData = result
    }
    
    // Felhasználói felület a userData alapján
}

Hogyan működik a LaunchedEffect a kulcsokkal

A LaunchedEffect legfontosabb mechanizmusa — a kulcsrendszer. A függvény első paramétere — vararg keys: Any? — meghatározza, hogy az effekt mikor induljon újra. A LaunchedEffect eltárolja a kulcsok előző értékeit, és minden összetételkor összehasonlítja azokat az újakkal. Ha legalább egy kulcs megváltozott (az equals()-en keresztül), a jelenlegi korutin megszakad és egy új indul.

Ha a kulcs például userId, akkor a felhasználó azonosítójának változásakor a LaunchedEffect automatikusan megszakítja a jelenlegi kérést és egy újat indít a frissített userId-val. Ez mentesíti a fejlesztőt az előző kérés kézi megszakítása és az adatok aktualitásának ellenőrzése alól — minden deklaratívan, kulcsokon keresztül van kezelve. Ez a megközelítés megfelel a Jetpack Compose reaktív paradigmájának.

Fontos szabály: ha állandót adunk át kulcsként — LaunchedEffect(Unit) — az effekt csak egyszer fut le a kompozícióba lépéskor, mint az onStart vagy onResume analógia a klasszikus Androidban. Ha nem adunk át kulcsokat — LaunchedEffect(Unit) — az effekt egyszer fut le. Ha üres zárójeleket adunk át — a LaunchedEffect nem fordul le, mert a kulcsok kötelező paraméterek.

kotlin
// Egyszeri végrehajtás a képernyő megjelenésekor
LaunchedEffect(Unit) {
    analytics.logScreenView("Profile")
}

// Újraindítás a userId változásakor
LaunchedEffect(userId) {
    loadUserData(userId)
}

// Több kulcs
LaunchedEffect(userId, filter, sortOrder) {
    fetchFilteredData(userId, filter, sortOrder)
}

Különbség a LaunchedEffect és a DisposableEffect között

Bár mindkét API a side effects körébe tartozik a Jetpack Compose-ban, a LaunchedEffect és a DisposableEffect alapvetően különböző feladatokat old meg. A LaunchedEffect aszinkron korutinokhoz való, kulcsok alapján történő újraindítási lehetőséggel, míg a DisposableEffect szinkron konfigurációs és erőforrás-tisztítási műveletekhez korutinok nélkül.

A fő különbség — az onDispose jelenléte a DisposableEffect-ben. A LaunchedEffect-nek nincs kifejezett tisztító blokkja: a korutin megszakítása automatikusan történik a kulcs változásakor vagy a kompozíció elhagyásakor, de a fejlesztő nem tud saját kódot beszúrni a megszakítás pillanatában. A DisposableEffect ezzel szemben rendelkezik egy onDispose blokkal, amely garantáltan végrehajtódik a kompozíció elhagyásakor, ami kritikus a natív erőforrások felszabadításához.

JellemzőLaunchedEffectDisposableEffect
VégrehajtásAszinkron (korutin)Szinkron
onDisposeNincs (a korutin automatikus megszakítása)Van (kifejezett tisztító blokk)
KulcsokÚjraindítás + régi korutin megszakításaonDispose végrehajtása + újrainicializálás
Tipikus alkalmazásHálózati kérések, Flow feliratkozások, időzítőkBroadcastReceiver, érzékelők, natív listener-ek
Megszakítás kilépéskorAutomatikusonDispose-on keresztül

A Google cikk „Compose Side Effects: Deep Dive” (2025) szerint a helyes választás a LaunchedEffect és a DisposableEffect között az erőforrás típusától függ: ha a művelet egy megszakítható korutin — használja a LaunchedEffect-et. Ha az erőforrás explicit close(), unregister() vagy dispose() hívást igényel — használja a DisposableEffect-et.

Adatok betöltése a LaunchedEffect segítségével

A LaunchedEffect leggyakoribb használati forgatókönyve — adatok betöltése a képernyő megnyitásakor. A minta egyszerű: a LaunchedEffect-en belül meghívódik a repository vagy UseCase suspend-függvénye, az eredmény egy state-változóhoz kerül, és a UI automatikusan újarajzolódik. A LaunchedEffect garantálja, hogy a képernyő újranyitásakor (például visszalépéskor) a betöltés újra megtörténik, ha a kulcsok megváltoztak.

A töltési állapotok megjelenítéséhez állapothármast használnak: Loading, Success, Error. A LaunchedEffect try-catch-be van csomagolva, és siker esetén state = Success(data), hiba esetén state = Error(exception) lesz beállítva. A UI reagál az állapotra és megjeleníti a megfelelő képernyőt: shimmer-töltő, adatok vagy hibaképernyő újrapróbálási gombbal.

Ha görgetéskor kell adatokat betölteni (pagináció), a LaunchedEffect-et összekapcsolják a LazyColumn és LazyListState elemekkel: a lista végének elérésekor a LaunchedEffect kulcs frissül (például az oldalszámláló), ami elindítja a következő adatrész betöltését.

kotlin
@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) }
    }
}

Kulcsok kezelése és újraindítás

A LaunchedEffect kulcsainak helyes használata — a kulcs a hatékony munkához az effektekkel (itt a szójáték tükrözi a lényeget). Ha a kulcs egy módosítható érték, amely gyakran változik (például a keresőlekérdezés szövege minden bevitt karakterkor), minden karakter megszakítja az előző korutint és elindít egy újat. A késleltetéssel történő kereséshez ez túlzás — jobb a debounce használata a korutinon belül.

A debounce megvalósításához a LaunchedEffect-en belül használja a delay()-t a fő művelet végrehajtása előtt. Például keresésnél: a LaunchedEffect(query) minden query változáskor elindul, de a kérés végrehajtása előtt delay(500) áll. Ha a felhasználó a következő karaktert 500 ms-nál korábban írja be, a korutin megszakad (a kulcs változása miatt) és egy új indul — így a kérés csak 500 ms szünet után kerül elküldésre.

Egy másik technika — a sealed class használata kulcsként. Ez lehetővé teszi annak pontos szabályozását, hogy az effekt mikor induljon újra. Például egy kulcs-csomagoló tartalmazza az azonosítót és egy kényszerített frissítési jelzőt: amikor a jelző false-ról true-ra változik, a LaunchedEffect újraindul, még akkor is, ha az azonosító nem változott. Ez a minta kényelmes a pull-to-refresh-hez.

kotlin
// Keresés debounce 500ms-sel
LaunchedEffect(searchQuery) {
    delay(500)
    searchResults.value = repository.search(searchQuery)
}

// Pull-to-refresh kényszerített frissítéssel
data class RefreshKey(val id: Int, val refreshTrigger: Int)
var refreshTrigger by remember { mutableIntStateOf(0) }

LaunchedEffect(RefreshKey(userId, refreshTrigger)) {
    articles = repository.loadUserArticles(userId)
}

Tipikus hibák a LaunchedEffect-tel

Az első és leggyakoribb hiba — a LaunchedEffect használata kulcsok nélkül. Ha a LaunchedEffect { ... } argumentumok nélkül íródik, a korutin minden összetételkor újraindul, ami végtelen kéréshurokhoz vezet. A LaunchedEffect legalább egy kulcsot igényel — általában Unit-ot az egyszeri végrehajtáshoz.

Második hiba — a LaunchedEffect használatának kísérlete Flow-ra való feliratkozáshoz collect nélkül. Ha a LaunchedEffect-en belül collect-et hívnak a Flow-n, a korutin felfüggesztődik a Flow befejeződéséig (ami a StateFlow esetében soha nem történik meg), és az onDispose blokk nem tud normálisan befejeződni. A helyes megközelítés — a collectLatest használata, amely megszakítja az előző gyűjteményt egy új érték érkezésekor.

Harmadik hiba — beágyazott objektumok átadása kulcsként. Ha a kulcs egy data class módosítható mezőkkel (var), a LaunchedEffect előfordulhat, hogy nem ismeri fel a változást, mert a Compose az equals()-et használja összehasonlításra, és az a var-mezők esetén kiszámíthatatlanul műkődhet. Mindig változtathatatlan objektumokat (val) vagy primitíveket használjon a LaunchedEffect kulcsaiként.

Gyakran Ismételt Kérdések

Mi történik, ha nem adunk át kulcsot a LaunchedEffect-nek?

Ha nem adunk át kulcsokat, a LaunchedEffect nem fordul le — a Kotlin legalább egy argumentumot követel a vararg paraméterekhez. Használja a LaunchedEffect(Unit)-ot egyszeri végrehajtáshoz a kompozícióba lépéskor, vagy adjon át konkrét értékeket, amelyek változásakor az effektnek újra kell indulnia.

Okozhat memóriaszivárgást a LaunchedEffect?

Nem, a LaunchedEffect automatikusan megszakítja a korutint, amikor a composable elhagyja a kompozíciót, ami megelőzi a memóriaszivárgásokat. Azonban ha a LaunchedEffect-en belüli korutin egy closure-en keresztül tartja a referenciát egy Activity-re vagy Context-re, szivárgás lehetséges — használja a viewModelScope-ot a hosszú életű műveletekhez a ViewModel-ben.

Mi a különbség a LaunchedEffect és a rememberCoroutineScope között?

A LaunchedEffect automatikusan végrehajtja a korutint a kompozícióba lépéskor, kulcsokhoz kötve. A rememberCoroutineScope egy scope-ot biztosít a korutinok kézi indításához, például válaszként az onItemClick-re. Használja a LaunchedEffect-et automatikus side effects-ekhez és a rememberCoroutineScope-ot korutinok indításához felhasználói események alapján.

Miért hajtódik többször végre a LaunchedEffect összetételkor?

Ha a LaunchedEffect kulcsának típusa instabil (például var vagy equals() nélküli osztály), a Compose előfordulhat, hogy nem ismeri fel, hogy az érték nem változott, és újraindítja az effektet minden összetételkor. Megoldás: használjon stabil típusokat (primitívek, stringek, data class val mezőkkel) vagy csomagolja a módosítható értékeket remember-be.

Hogyan állítható le kézzel a LaunchedEffect?

Nincs közvetlen mód a LaunchedEffect külről történő leállítására — a vezérlés kulcsokon keresztül történik. Változtassa meg a kulcsot a jelenlegi korutin megszakításához. Ha teljes kontrollra van szüksége a korutin életciklusa felett, használja a rememberCoroutineScope-ot Job-bal, és kézzel hívja a job.cancel()-t esemény vagy állapotváltozáskor.

Összefoglaló

  • LaunchedEffect — Jetpack Compose side-effect API aszinkron korutinok indításához, amelyek a composable életciklusához vannak kötve.
  • Kulcsok — újraindítási rendszer kulcsok alapján: a kulcs változása megszakítja a jelenlegi korutint és egy újat indít frissített paraméterekkel.
  • Automatikus megszakítás — a korutin automatikusan megszakad, amikor a composable elhagyja a kompozíciót, megelőzve a memóriaszivárgásokat.
  • Adatok betöltése — tipikus minta: LaunchedEffect(key) adatok betöltéséhez a hálózatról a Loading, Success, Error állapotok kezelésével.
  • Debounce — delay()-en keresztül valósul meg a LaunchedEffect-en belül: ha a kulcs a késleltetés lejárta előtt változik, a korutin megszakad.
  • Flow feliratkozás — használja a collectLatest-et a collect helyett a Flow helyes feldolgozásához a LaunchedEffect-en belül.
  • Stabil kulcsok — csak változtathatatlan típusokat (val, primitívek, data class) használjon kulcsként a kiszámítható viselkedés érdekében.

Kulcsrakész mobilalkalmazást fejlesztünk

Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.

Projekt megbeszélése

Olvassa el is