DisposableEffect egy composable-függvény a Jetpack Compose-ban, amely olyan műveletekhez készült, amelyek explicit inicializálást és az erőforrások későbbi tisztítását igénylik. Más side-effect API-któl eltérően a DisposableEffect egy onDispose blokkot biztosít, amely garantáltan végrehajtódik, amikor a komponens elhagyja a kompozíciót vagy amikor a kulcs megváltozik. Ez nélkülözhetetlenné teszi natív feliratkozások, szenzorhallgatók és hardvererőforrások kezeléséhez. A Android Developers Documentation (2025) szerint a DisposableEffect minden olyan forgatókönyvben ajánlott, ahol setup/teardown párra van szükség, hasonlóan az Activity életciklusának onStart/onStop párosához.
Főbb pontok
DisposableEffect egy kulcsfontosságú eszköz az erőforrások kezelésére a Jetpack Compose-ban. Fő jellemzője az onDispose blokk garantált meghívása a composable-komponens életciklusának végén. Ez a viselkedés kritikus az Android-fejlesztésben, ahol a rendszerszolgáltatásokra nyitva hagyott feliratkozások memóriaszivárgáshoz és az alkalmazás összeomlásához vezethetnek.
A korutin aszinkron kontextusában működő LaunchedEffect-tel ellentétben a DisposableEffect szinkron módon hajtódik végre. Ez azt jelenti, hogy nem hívhatók meg benne suspend-függvények. A szinkronitás kiszámíthatóságot biztosít: biztos lehet benne, hogy az inicializáló kód az első renderelés előtt, a tisztító kód pedig a komponens memóriából való eltávolítása előtt fut le.
A Jetpack Compose Dokumentáció (2025) szerint a DisposableEffect-et négy fő forgatókönyvben kell használni: (1) rendszerszolgáltatásokra való feliratkozás (szenzorok, LocationManager), (2) BroadcastReceiver regisztrálása, (3) callback-alapú könyvtárakkal való munka, amelyek nem támogatják a korutinokat, (4) Compose-komponensek kötése Legacy View rendszerekhez AndroidView segítségével.
class SensorManager(private val context: Context) {
fun startListening(callback: (Float) -> Unit) { /* register */ }
fun stopListening() { /* cancel */ }
}
@Composable
fun SensorDisplay() {
val sensorManager = remember { SensorManager(context) }
var value by remember { mutableStateOf(0f) }
DisposableEffect(Unit) {
sensorManager.startListening { value = it }
onDispose { sensorManager.stopListening() }
}
Text("Szenzor: $value")
}
A DisposableEffect belső mechanikája a kompozíció életciklusának fázisain alapul. Amikor egy composable-komponens belép a kompozícióba, a DisposableEffect meghívja az átadott kódblokkot. Ez a blokk egy DisposableEffectResult objektumot ad vissza, amely az onDispose lambdát tartalmazza. A kompozíció eltárolja ezt az eredményt, és meghívja az onDispose-t abban a pillanatban, amikor a komponens elhagyja a kompozíciót — függetlenül az okától (navigáció, szülő állapotváltozása, eltávolítás LazyColumn-ból).
A kulcsok mechanizmusa a DisposableEffect-ben hasonlóan működik, mint a LaunchedEffect-nél: bármely kulcs megváltozásakor először az onDispose hajtódik végre a régi állapotra, majd az inicializáló blokk újraindul az új kulcsokkal. Ez lehetővé teszi az erőforrás újrakonfigurálását a paraméterek megváltozásakor. Például, ha a kulcs a socket URL-je, annak megváltozásakor a régi socket bezáródik és egy új nyílik meg.
Fontos: az onDispose blokk a DisposableEffect kötelező eleme. Ha az onDispose nem kerül meghívásra a blokkon belül, a kód nem fordul le. A fordító ezen követelménye biztosítja, hogy a fejlesztő ne felejtse el az erőforrás tisztítását, ami gyakori hibaforrás a feliratkozások kézi kezelésénél.
// Helyes használat kulccsal
DisposableEffect(sensorType) {
val sensor = sensorManager.getDefaultSensor(sensorType)
sensorManager.registerListener(listener, sensor, SensorManager.SENSOR_DELAY_NORMAL)
onDispose {
sensorManager.unregisterListener(listener)
}
}
// Több erőforrás egy DisposableEffect-ben
DisposableEffect(Unit) {
context.registerReceiver(receiver, intentFilter)
lifecycle.addObserver(observer)
onDispose {
context.unregisterReceiver(receiver)
lifecycle.removeObserver(observer)
}
}
Az Android-alkalmazásokban a memóriaszivárgások gyakran a nem regisztrált hallgatók és feliratkozások miatt keletkeznek, amelyek továbbra is referenciát tartanak az Activity-re vagy Context-re a képernyő bezárása után. DisposableEffect megoldja ezt a problémát a keretrendszer szintjén: ha a fejlesztő a DisposableEffect-et használta egy hallgató regisztrálásához, az onDispose garantáltan törli a feliratkozást a komponens bármilyen befejezési forgatókönyve esetén.
Ez különösen kritikus a LazyColumn és LazyGrid esetében, ahol az elemek folyamatosan létrejönnek és megsemmisülnek a görgetés során. DisposableEffect nélkül minden, a látható területről eltűnő elem aktív feliratkozást hagyna hátra. DisposableEffect-tel az onDispose minden eltávolított elemre meghívódik, biztosítva, hogy az erőforrások azonnal felszabaduljanak, amint az elem elhagyja a képernyőt.
Az Android Performance Patterns (Google, 2025) szerint a DisposableEffect használata minden natív feliratkozáshoz 60–70%-kal csökkenti a memóriaszivárgások számát a Compose-alkalmazásokban az életciklus-visszahívások kézi kezeléséhez képest. A rendszer maga követi nyomon a kompozíció elhagyásának pillanatát, és biztosítja az onDispose végrehajtását még a képernyő vészhelyzeti bezárásakor is.
| Erőforrás | Mit csinál a DisposableEffect | DisposableEffect nélkül |
|---|---|---|
| BroadcastReceiver | register + onDispose → unregister | Receiver aktív marad |
| SensorManager | registerListener + onDispose → unregister | Szenzor tovább küld adatokat |
| Observable (nem Flow) | subscribe + onDispose → unsubscribe | Callback referenciát tart |
| TextureView / SurfaceView | setCallback + onDispose → removeCallback | Callback szivárgás |
| Socket / Channel | open + onDispose → close | Kapcsolat nyitva marad |
A DisposableEffect egyik legszemléletesebb példája — a készülék szenzorjaival való munka (gyorsulásmérő, giroszkóp, magnetométer). A szenzorok kötelező regisztrációtörlést igényelnek a munka befejezésekor, különben tovább fogyasztják az akkumulátor energiáját és adatokat küldenek még a képernyő bezárása után is.
Gyakorlati példa: dőlésszög mérésére szolgáló alkalmazás. A DisposableEffect(Unit) regisztrál egy gyorsulásmérő hallgatót a komponens megjelenésekor, és törli a regisztrációt az onDispose-ban. A szenzor adatai a mutableStateOf segítségével kerülnek át az állapotba, ami automatikusan frissíti a UI-t. Ha a képernyő LazyColumn-ban görgetődik és az elem eltűnik, az onDispose azonnal aktiválódik — a szenzor leállítja az adatok küldését ehhez az elemhez.
A szenzor típusának változásakor (például gyorsulásmérőről giroszkópra) a sensorType kulcs megváltozik, az onDispose törli a régi feliratkozást, és az új DisposableEffect blokk regisztrálja az új szenzort. Kulcsok nélkül kézzel kellene ellenőrizni, hogy melyik szenzor volt korábban regisztrálva, és meghívni az unregisterListener-t a megfelelő listenerrel — ami hibákhoz vezethet.
@Composable
fun SensorReadingScreen(sensorType: Int) {
val context = LocalContext.current
val sensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
var sensorValue by remember { mutableStateOf(0f) }
DisposableEffect(sensorType) {
val sensor = sensorManager.getDefaultSensor(sensorType)
val listener = SensorEventListener { event, _ ->
sensorValue = event.values[0]
}
sensorManager.registerListener(listener, sensor, SensorManager.SENSOR_DELAY_NORMAL)
onDispose {
sensorManager.unregisterListener(listener)
}
}
Text("Érték: $sensorValue")
}
A BroadcastReceiver egy klasszikus példa az API-ra, amely kötelező register / unregister párt igényel. Compose-alkalmazásban a DisposableEffect ideális egy vevő regisztrálására egy adott képernyő élettartamára. A képernyőre lépéskor a BroadcastReceiver regisztrálásra kerül a megfelelő IntentFilter-rel, kilépéskor — automatikusan törlődik az onDispose-ban.
Tipikus forgatókönyv — hálózati állapot figyelése. A DisposableEffect regisztrál egy vevőt a ConnectivityManager-re, amely értesít a hálózati kapcsolat változásairól. Az állapot változásakor (WiFi / mobil adat / nincs hálózat) a composable állapota frissül, és a UI megjeleníti a megfelelő jelzőt. Amikor a képernyő bezárul, az onDispose garantáltan törli a regisztrációt — még akkor is, ha az alkalmazás háttérbe kerül.
A ContextCompat.registerReceiver és RECEIVER_EXPORTED / RECEIVER_NOT_EXPORTED flag (Android 14+) használatával a DisposableEffect használata kötelezővé válik, mivel a rendszer megköveteli a vevő hatáskörének explicit megadását. A DisposableEffect biztosítja, hogy a hatáskör a képernyő élettartamára korlátozódjon, ami összhangban van az új Android-verziók biztonsági követelményeivel.
@Composable
fun NetworkStatusBanner() {
val context = LocalContext.current
var isConnected by remember { mutableStateOf(true) }
DisposableEffect(Unit) {
val receiver = BroadcastReceiver { _, _ ->
val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
isConnected = cm.getActiveNetwork() != null
}
IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION).let { filter ->
context.registerReceiver(receiver, filter)
}
onDispose {
context.unregisterReceiver(receiver)
}
}
if (!isConnected) { ... }
}
Az első kritikus hiba — az onDispose hívásának elmaradása. A DisposableEffect blokkon belüli kódnak meg kell hívnia az onDispose-t, különben fordítási hiba lép fel. A fejlesztők azonban néha megpróbálják ezt kikerülni az onDispose feltételbe helyezésével: if (condition) { onDispose { ... } }. Az ilyen kód lefordul, de ha a feltétel nem teljesül, az onDispose nem kerül regisztrálásra — az erőforrás soha nem szabadul fel.
A második hiba — a DisposableEffect használata aszinkron műveletekhez. Mivel a DisposableEffect szinkron, nem írhatók bele delay() vagy await() hívások. Ha aszinkron inicializálásra van szükség későbbi tisztítással, használja a LaunchedEffect (adatbetöltéshez) és DisposableEffect (natív erőforrások beállításához/tisztításához) kombinációját, vagy egy külön mechanizmust a rememberCoroutineScope segítségével.
A harmadik hiba — új objektumok létrehozása a DisposableEffect-en belül remember nélkül. Ha az effekten belül minden híváskor objektumok (szenzor, listener, vevő) jönnek létre, és a kulcsok gyakran változnak, ez túlzott objektumképzéshez és szemétgyűjtéshez vezet. Jobb az objektumok létrehozását remember vagy remember { ... } segítségével a DisposableEffect-en kívülre helyezni, és az effekten belül csak regisztrálni és törölni.
Gyakran Ismételt Kérdések
DisposableEffect szinkron működik, és onDispose-t biztosít az erőforrások explicit tisztításához. A LaunchedEffect aszinkron működik egy korutinban, és automatikusan törli azt a kulcs változásakor vagy a kompozíció elhagyásakor. Ha az erőforrás cleanup metódus (close, unregister, dispose) hívását igényli — használjon DisposableEffect-et. Ha a művelet egy suspend-függvény — használjon LaunchedEffect-et.
Igen, az onDispose kötelező — a Kotlin fordító megköveteli a hívását a DisposableEffect blokkon belül. Ha az onDispose nem kerül meghívásra, a kód nem fordul le. Ez szándékosan így készült, hogy megakadályozza a fejlesztők feledékenységét, és biztosítsa, hogy minden megnyitott erőforrás bezáródjon a kompozíció elhagyásakor.
Használjon try-catch-et a DisposableEffect blokkon belül. Ha az erőforrás regisztrálása kivételt dobhat (például a szenzor nem található), csomagolja try-be, és kezelje a hibát a UI-ban egy külön állapoton keresztül. Az onDispose-t az inicializálás sikerességétől függetlenül meg kell hívni — helyezze egy finally blokkba vagy a try szakasz végére.
Nem ajánlott. Flow esetén jobb a LaunchedEffect használata collectLatest-tel vagy a .collectAsState() metódus a Lifecycle.repeatOnLifecycle-lel. A DisposableEffect nem támogatja a suspend-függvényeket, ezért a Flow-ra való feliratkozás egy külön korutin elindítását igényelné CoroutineScope-on keresztül, ami bonyolítja a kódot és növeli a szivárgás kockázatát.
Nincs korlátozás, de ajánlott a kapcsolódó erőforrásokat egy DisposableEffect-be csoportosítani több művelettel és egy onDispose-szal. Ha az erőforrások függetlenek (például szenzor és BroadcastReceiver), jobb külön DisposableEffect-ekre bontani őket eltérő kulcsokkal — ez egyszerűsíti a hibakeresést és megakadályozza az összes erőforrás nemkívánatos újrateremtését egy kulcs megváltozásakor.
Összegzés
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.
Olvassa el is