SideEffect — mi ez, állapotszinkronizáció a Jetpack Compose-ban

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

SideEffect — egy composable függvény a Jetpack Compose-ban, amely minden sikeres recompozíciónál végrehajtja a megadott kódblokkot. A LaunchedEffect és DisposableEffect függvényekkel ellentétben a SideEffect nincs kulcsokhoz kötve, és nincs tisztítási blokkja — egyszerűen szinkronizálja a Compose-állapotot külső rendszerekkel minden renderelés után. Ez ideálissá teszi callback függvények frissítésére, ViewPager-rel való szinkronizációra és adatok Analytics SDK-ba történő továbbítására. A Android Developers Documentation (2025) szerint a SideEffect szigorúan azután hajtódik végre, hogy a Compose megerősítette a sikeres recompozíciót, és nem hajtódik végre, ha a recompozíció kimaradt.

Főbb pontok

  • SideEffect — side-effect API minden sikeres recompozíció után végrehajtott kódhoz.
  • Szinkronizáció — Compose-állapotot továbbít külső rendszerekbe, amelyek nem támogatják a Compose-t.
  • Kulcsok nélkül — a LaunchedEffect-tel ellentétben a SideEffect nem indul újra, hanem minden recompozíciónál végrehajtódik.
  • Nincs tisztítás — a SideEffect nem biztosít onDispose-t, csak egyirányú szinkronizációra szolgál.
  • Szinkron — a blokk szinkron módon hajtódik végre a Compose kompozíciós fázisával, coroutine nélkül.

Mi az a SideEffect a Jetpack Compose-ban

SideEffect — a legegyszerűbb side-effect API a Jetpack Compose-ban. Végrehajt egy kódblokkot egy composable komponens minden sikeres recompozíciójánál. A „sikeres” szó itt kulcsfontosságú: ha a Compose úgy dönt, hogy a recompozíció nem szükséges (például az összes bemeneti paraméter nem változott, és az eredmény ugyanaz lesz), a SideEffect nem hajtódik végre. Ez garantálja, hogy a szinkronizációs blokk csak akkor hívódik meg, amikor a UI ténylegesen megváltozott.

A SideEffect fő használati forgatókönyve — a Compose-állapot szinkronizálása olyan objektumokkal, amelyek nem részei a Compose-fának. Tipikus példák: callback függvény frissítése a Legacy View rendszerben, aktuális állapot továbbítása a ViewPager-be, esemény küldése az Analytics SDK-nak a megjelenített adatok változásakor, szinkronizáció térkép SDK-kkal, amelyek külső formátumban várják a frissítéseket.

A Android Developer Blog (2025) szerint a SideEffect gyakran használatos a remember függvénnyel kombinálva: a remember tárol egy objektumot (például callback-et), a SideEffect pedig frissíti azt minden függőségi változáskor. Ez a minta különösen fontos azoknál a könyvtáraknál, amelyek listener objektumokat fogadnak el, és nem hozzák létre újra azokat frissítéskor — SideEffect nélkül a listener elavult hivatkozást tartalmazna az aktuális állapotra.

kotlin
@Composable
fun MapScreen(zoomLevel: Int, markers: List<Marker>) {
    val mapView = remember { MapView(LocalContext.current) }
    
    SideEffect {
        mapView.setZoom(zoomLevel)
        mapView.updateMarkers(markers)
    }
    
    AndroidView(factory = { mapView })
}

Hogyan működik a SideEffect és a kompozíciós fázisok

A SideEffect megértéséhez ismerni kell a Jetpack Compose végrehajtási fázisait. A Compose három fázison megy keresztül minden képkockánál: Composition (mit jelenítsen meg), Layout (hol jelenítse meg), Drawing (hogyan jelenítse meg). A SideEffect a Composition fázis végén hajtódik végre — miután az összes composable függvény lefutott, de a Layout fázis előtt. Ez garantálja, hogy a SideEffect a recompozíció után az összes változó végső állapotát látja.

Ez az elhelyezkedés az életciklusban fontos előnyt biztosít: a SideEffect nem okozhat végtelen recompozíciót, még akkor sem, ha az állapot megváltozik benne. Mivel a kompozíció után hajtódik végre, a SideEffect-en belül végrehajtott változtatások csak a következő képkockában kerülnek figyelembevételre — ez megakadályozza a composable függvény törzsében bekövetkező változásokra jellemző hurkokat (amikor a setState a kompozíción belül új kompozíciót indít el a jelenlegi befejezése előtt).

Egy másik jellemző — a SideEffect nem optimalizálható kulcsokkal. Minden recompozíciónál végrehajtódik, függetlenül attól, hogy melyik állapot változott meg. Ha pontosabb vezérlésre van szükség (csak egy adott paraméter változásakor hajtódjon végre), használjon LaunchedEffect-et kulcsokkal, vagy csomagolja be a SideEffect-et egy remember-en keresztüli változásellenőrzésbe.

kotlin
// SideEffect optimalizálva remember-rel
var currentZoom by remember { mutableIntStateOf(zoomLevel) }

SideEffect {
    if (currentZoom != zoomLevel) {
        map.animateToZoom(zoomLevel)
        currentZoom = zoomLevel
    }
}

// Ezen ellenőrzés nélkül a SideEffect meghívná az animateToZoom-ot
// minden recompozíciónál, még akkor is, ha a zoomLevel nem változott

Callback függvények frissítése SideEffect segítségével

A SideEffect leggyakoribb gyakorlati forgatókönyve — az aktuális állapotot lezáró callback függvények frissítése. Ezt a Jetpack Compose-ban “callback lifecycle management”-nek nevezik. A probléma az, hogy a Kotlin lambda kifejezései a változókat referenciával fogják be, és ha egy callback egy változó egyik értékével jött létre, majd a változó megváltozott — a callback továbbra is a régi értéket használja.

Tekintsünk egy példát: a Google Maps SDK Androidhoz az OnCameraMoveListener objektumot a setOnCameraMoveListener() segítségével fogadja el. Ha átad egy lambdát, amely bekapja az isTrackingEnabled értéket, akkor amikor az isTrackingEnabled megváltozik, a lambda nem frissül — a Maps SDK továbbra is a régi callback-et hívja elavult adatokkal. A SideEffect megoldja ezt a problémát: minden recompozíciónál újra beállítja a listenert, garantálva, hogy az SDK mindig a friss lambdát használja az aktuális állapottal.

A Maps SDK dokumentációja Androidhoz (2025) szerint a Google pontosan ezt a mintát ajánlja a Maps Jetpack Compose-hoz való integrálásakor. Hasonló megközelítést használnak a WebView, VideoView, TextureView és bármely más View-alapú komponens esetében, amelyek set-metódusokon keresztül fogadnak callback-eket. A SideEffect garantálja a callback-ek aktualitását minden állapotváltozáskor.

kotlin
@Composable
fun MapComposable(isTrackingEnabled: Boolean, onMarkerClick: (Marker) -> Unit) {
    val mapView = remember { MapView(LocalContext.current) }
    
    SideEffect {
        mapView.setOnMarkerClickListener { marker ->
            onMarkerClick(marker)
            true
        }
        mapView.isTrafficEnabled = isTrackingEnabled
    }
    
    AndroidView(factory = { mapView })
}

Szinkronizáció az Analytics SDK-val

A SideEffect másik fontos forgatókönyve — események küldése analitikai rendszerekbe a UI állapotának változásakor. Például amikor a felhasználó füleket vált a TabLayout-ban egy Compose-képernyőn belül, a SideEffect továbbíthatja a kiválasztott fül aktuális állapotát a Firebase Analytics vagy AppsFlyer felé. Minden alkalommal, amikor a kiválasztott fül megváltozik (és recompozíció történik), a SideEffect elküldi a megfelelő eseményt.

A különbség az események közvetlen onClick vagy onTabSelected belüli küldéséhez képest az, hogy a SideEffect bármilyen módon okozott állapotváltozásra reagál — nemcsak a felhasználói műveletre, hanem programozott változtatásra, állapotvisszaállításra képernyőelforgatás után vagy Deep Link-re is. Ez teszi a SideEffect-et univerzális szinkronizációs mechanizmussá, amely független a változás forrásától.

A Firebase Best Practices (Google, 2025) szerint az analitikai események SideEffect-en keresztüli küldése teljesebb képet ad a felhasználói útról, mivel rögzíti az összes állapotváltozást, beleértve azokat is, amelyek közvetlen felhasználói beavatkozás nélkül történnek. Fontos azonban nem túlzásba esni: minden esemény az Analytics-ben hálózati kérés, ezért a gyakran változó állapotokhoz (görgetési pozíció, ujj koordináták) a SideEffect nem megfelelő — használjon debounce-t, vagy csak jelentős változásoknál küldjön eseményeket.

kotlin
@Composable
fun ProductScreen(selectedTab: ProductTab, productId: String) {
    val firebaseAnalytics = remember { FirebaseAnalytics.getInstance(LocalContext.current) }
    
    SideEffect {
        val params = Bundle().apply {
            putString(FirebaseAnalytics.Param.CONTENT_TYPE, selectedTab.name)
            putString(FirebaseAnalytics.Param.ITEM_ID, productId)
        }
        firebaseAnalytics.logEvent(        FirebaseAnalytics.Event.VIEW_ITEM, params)
    }
    
    // UI TabRow-val és kiválasztott füllel
}

SideEffect vs LaunchedEffect: mikor melyiket használjuk

A SideEffect és LaunchedEffect közötti választás két tényezőtől függ: szükség van-e aszinkronitásra, és szükség van-e kulcsokkal történő vezérlésre. A SideEffect szinkron és minden recompozíciónál végrehajtódik. A LaunchedEffect aszinkron (coroutine) és csak a kulcs megváltozásakor hajtódik végre, nem minden recompozíciónál.

Ha minden UI-változáskor végre kell hajtani egy műveletet — használja a SideEffect-et. Ha egy műveletet egyszer kell végrehajtani a képernyő megjelenésekor vagy egy adott paraméter változásakor — használja a LaunchedEffect-et kulcsokkal. Ha aszinkron műveletre van szükség (adatok betöltése, késleltetés, Flow-val való munka) — csak LaunchedEffect, mert a SideEffect nem támogatja a suspend függvényeket.

JellemzőSideEffectLaunchedEffect
VégrehajtásMinden recompozíciónálKulcsok változásakor
AszinkronitásSzinkronCoroutine
KulcsokNincsVan (vararg)
TisztításNincsCoroutine automatikus megszakítása
Tipikus használatCallback-ek, Analytics, View szinkronizációBetöltés, Flow feliratkozás, időzítők

A gyakorlatban a side effects használatának 70%-át a LaunchedEffect (aszinkron műveletek, adatbetöltés), 20%-át a DisposableEffect (tisztítással járó erőforrások) és csak 10%-át a SideEffect (callback függvények szinkronizációja) fedi le. A SideEffect egy speciális eszköz a feladatok szűk köréhez, de ezekben a feladatokban pótolhatatlan.

Gyakori hibák a SideEffect-tel

A fő hiba — a Compose-állapot megváltoztatása a SideEffect-en belül. Bár a SideEffect nem okoz közvetlenül végtelen hurkot (mivel a kompozíciós fázis után hajtódik végre), túlzott recompozíciókat okozhat. Ha az állapot (mutableStateOf) megváltozik a SideEffect-en belül, ez új recompozíciót indít el a következő képkockában, amely újra végrehajtja a SideEffect-et — és így tovább a stabilizálódásig. Ez nem végtelen hurok, de extra munka a keretrendszer számára.

A második hiba — nehéz számítások végrehajtása a SideEffect-en belül. Mivel a SideEffect minden recompozíciónál meghívódik, és a recompozíciók másodpercenként több tucatszor is megtörténhetnek (animációknál, görgetésnél), bármilyen nehéz kód a SideEffect-en belül képkocka-kieséshez vezet. Vigye ki a nehéz műveleteket a kompozícióból — coroutine-ba (LaunchedEffect) vagy számítsa ki derivedStateOf / remember segítségével.

A harmadik hiba — a SideEffect használatának kísérlete aszinkron kódhoz. A SideEffect nem suspend függvény, ezért a delay(), await(), collect() és más coroutine műveletek benne nem fordulnak le. Ha aszinkron műveletet kell végrehajtani recompozíció után, használja a snapshotFlow { ... } kombinációját a LaunchedEffect-tel, vagy indítson coroutine-t a rememberCoroutineScope segítségével.

Gyakran Ismételt Kérdések

Végrehajtódik a SideEffect az első kompozíciónál?

Igen, a SideEffect minden sikeres kompozíciónál végrehajtódik, beleértve az elsőt is — amikor a komponens először jelenik meg a képernyőn. Ez különbözteti meg a LaunchedEffect(Unit)-tól, amely szintén egyszer végrehajtódik az első kompozíciónál, de a későbbi recompozícióknál nem (ha a kulcs nem változott).

Okozhat a SideEffect végtelen hurkot?

Nem, a SideEffect a kompozíciós fázis után hajtódik végre — a benne végrehajtott változtatások csak a következő képkockában kerülnek alkalmazásra, ami megakadályozza a hurkokat. Az állapot gyakori megváltoztatása a SideEffect-en belül azonban recompozíciók lavináját okozhatja, csökkentve a teljesítményt. Csak akkor változtassa meg az állapotot a SideEffect-en belül, ha valóban szükséges.

Mi a különbség a SideEffect és a snapshotFlow között?

A SideEffect szinkron módon hajtódik végre minden recompozíciónál. A snapshotFlow Flow-t hoz létre a Compose-állapotból, és használható a collectLatest-tel a LaunchedEffect-ben a változások reaktív feldolgozásához. A snapshotFlow azokra az esetekre alkalmas, amikor debounce, filter vagy distinctUntilChanged segítségével kell reagálni a változásokra — ami a szinkron SideEffect-ben lehetetlen.

Hogyan lehet hibakeresni a SideEffect-et, ha túl gyakran hajtódik végre?

Használja az Android Studio Compose Modifier Debugger-t, vagy adjon hozzá naplózást a komponens nevével és a hívások gyakoriságával. Ha a SideEffect gyakrabban hajtódik végre a vártnál, ellenőrizze, hogy a szülő komponens állapota nem változik-e szükségtelenül. Optimalizálás: különítse el a UI stabil részeit külön composable függvényekbe unstable annotációkkal a recompozíciók számának csökkentése érdekében.

Kombinálható a SideEffect a DisposableEffect-tel?

Igen, használhatók ugyanabban a komponensben különböző célokra. A DisposableEffect felelős az erőforrás beállításáért és tisztításáért (egyszer), a SideEffect pedig az aktuális állapot ezzel az erőforrással való szinkronizálásáért minden recompozíciónál. Tipikus példa: a DisposableEffect regisztrál egy callback-et az API-n keresztül, a SideEffect pedig frissíti a bezárt adatokat ebben a callback-ben minden változáskor.

Összefoglalás

  • SideEffect — side-effect API szinkron kódhoz, amely minden sikeres recompozíció után hajtódik végre a Jetpack Compose-ban.
  • Szinkronizáció — a fő forgatókönyv: Compose-állapot átadása külső rendszereknek (Google Maps, WebView, ViewPager, Analytics SDK).
  • Callback-ek — a SideEffect garantálja az aktuális állapotot lezáró callback függvények aktualitását minden UI-frissítésnél.
  • Hurkok nélkül — a kompozíciós fázis után hajtódik végre, így az állapot megváltoztatása a SideEffect-en belül nem okoz végtelen recompozíciót.
  • Korlátozások — nem támogatja a kulcsokat, aszinkronitást és tisztítási blokkot; ezekhez a feladatokhoz használja a LaunchedEffect-et vagy a DisposableEffect-et.
  • Teljesítmény — kerülje a nehéz számításokat a SideEffect-en belül, mivel minden recompozíciónál végrehajtódik (akár 60-szor másodpercenként).
  • Hibakeresés — ellenőrizze a hívások gyakoriságát a Compose Debugger segítségével, és optimalizálja a remember-en keresztül a szükségtelen recompozíciók szűréséhez.

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