MutableState — megfigyelhető állapot és frissítési mechanizmus a Compose-ban

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

MutableState — egy interfész a Jetpack Compose-ban, amely egy tárolót képvisel egy változtatható megfigyelhető érték számára. Ez a Compose reaktív rendszerének alapja: minden alkalommal, amikor a MutableState értéke változik a setteren keresztül, a Compose Runtime értesíti az összes olvasó komponenst és elindítja a recompozíciót. A Google Android Developers, 2026 szerint a MutableState megértése kötelező a deklaratív UI-ban való helyes állapotkezeléshez.

Főbb pontok

  • MutableState — compose.runtime interfész egy value tulajdonsággal (getter + setter)
  • State — szülő interfész csak olvasásra, a MutableState írási lehetőséget ad hozzá
  • Recompozíció a value setterének meghívásakor indul egy aktív snapshot cikluson belül
  • SnapshotMutationPolicy határozza meg, mikor számít a változás jelentősnek a recompozíció szempontjából
  • MutableIntState és társai — optimalizált primitív verziói a MutableState-nek

Mi az a MutableState a Jetpack Compose-ban

MutableState — egy interfész az androidx.compose.runtime csomagból, amely egy tulajdonságot deklarál: override var value: T. A getter visszaadja az aktuális értéket, a setter egy újat ír és értesíti a Compose Runtime-ot a változásról. Az interfész a State<T>-ből örököl, ahol a value csak olvasható. Ez a kétszintű architektúra lehetővé teszi a hozzáférés szétválasztását: a komponens, amelynek csak olvasnia kell az értéket, State<T>-t kap, a tulajdonos komponens pedig — MutableState<T>-t.

A MutableState alapértelmezett megvalósítása a belső SnapshotMutableStateImpl osztály, amely a snapshot mechanizmust használja a változások követésére. Amikor a value setter meghívásra kerül, az aktuális snapshot rögzíti az írást és az összes regisztrált ObservedScope-ot (megfigyelési területet) érvénytelenként jelöli meg. Ezek a területek (általában Composable függvények) a következő képkockában újra lesznek komponálva. Az egész folyamat szinkronban és blokkolás nélkül zajlik a Lock-free snapshot architektúrának köszönhetően.

State vs MutableState: A State — egy csak olvasható interfész, amely a komponensek nyilvános API-jaihoz használatos. Amikor egy Composable függvény paraméterét State<Int>-ként deklarálja, garantálja, hogy a komponens olvashatja, de nem módosíthatja az állapotot. A MutableState a tulajdonos komponensen belül használatos. Az ilyen szétválasztás — a Compose egyik alapvető gyakorlata — megakadályozza a jogosulatlan módosításokat.

A State, MutableState és származtatott interfészek hierarchiája

Az állapotinterfészek hierarchiájának a Compose-ban több szintje van. A csúcson — State<T> csak olvasható value-val. Alatta — MutableState<T> olvasható-írható value-val. Ezután jönnek a specializált primitív verziók: MutableIntState, MutableFloatState, MutableLongState, MutableBooleanState és mások, amelyek elkerülik a primitívek automatikus csomagolását (boxing).

MutableDoubleState és MutableLongState — kevésbé elterjedt, de létező típusok. Gyűjtemény interfészek: MutableListState — a lista belső változásainak követésére, MutableStateMap — térképekhez. Ezen interfészek mindegyike egy adott forgatókönyvre van optimalizálva, és kiterjeszti az alap MutableState-et további gyűjteménykezelési metódusokkal.

SnapshotStateList és SnapshotStateMap — a módosítható listák és térképek snapshotokkal kompatibilis megvalósításai. Lehetővé teszik nemcsak az érték cseréjének, hanem a belső változásoknak a követését is: elem hozzáadása a listához, törlés, meglévő elem módosítása. Az ilyen struktúrákhoz a mutableStateListOf() és mutableStateMapOf() hozza létre a megfelelő megfigyelhető gyűjteményeket.

InterfészRendeltetésLétrehozási metódus
State<T>Csak olvasható tároló
MutableState<T>Olvasható-írható tárolómutableStateOf()
MutableIntStatePrimitív Int boxing nélkülmutableIntStateOf()
MutableFloatStatePrimitív Float boxing nélkülmutableFloatStateOf()
SnapshotStateListMegfigyelhető listamutableStateListOf()
SnapshotStateMapMegfigyelhető térképmutableStateMapOf()

SnapshotMutationPolicy: mikor indítsuk el a recompozíciót

SnapshotMutationPolicy — egy interfész, amely meghatározza, hogy a MutableState változása mikor számít jelentősnek. A mutableStateOf második argumentumként fogadja a policy-t. Alapértelmezett megvalósítások: structuralEquality() (equals), referentialEquality() (===), neverEqualPolicy() (mindig jelentősnek tekinti a változást). Egyéni logikához saját policy-t is megvalósíthat.

structuralEquality() — alapértelmezett viselkedés. A Compose összehasonlítja az új értéket a régivel equals() segítségével. Ha az eredmény true — a recompozíció NEM indul el. Ez kényelmes primitívek és data class-ok esetén, ahol két azonos mezőkkel rendelkező példány egyenlőnek számít. Probléma: ha a data class List-et tartalmaz, az equals() mély összehasonlítást végez, ami nagy listák esetén költséges lehet.

referentialEquality() — referenciákat hasonlít össze === segítségével. A recompozíció csak akkor indul el, ha egy másik objektumot rendelünk hozzá, még akkor is, ha a tartalom azonos. Ez optimális a megváltoztathatatlan data class-okhoz, ahol minden új példány garantáltan változást jelent. neverEqualPolicy() — mindig jelentősnek tekinti a változást, összehasonlítás nélkül. Hasznos, ha a setter ritkán kerül meghívásra és nem kell időt pazarolni az equals-re.

kotlin
    // Irányelvek összehasonlítása a gyakorlatban
data class User(val name: String, val age: Int)

@Composable
fun UserProfile() {
    // structuralEquality: recompozíció CSAK ha az adatok változtak
    var user1 by remember {
        mutableStateOf(User("Alice", 30))
    }

    // referentialEquality: recompozíció MINDEN hozzárendeléskor
    var user2 by remember {
        mutableStateOf(User("Bob", 25),
            SnapshotMutationPolicy.referentialEquality())
    }

    // user1: copy() azonos mezőkkel NEM vált ki recompozíciót
    // user2: még a user2.copy() == user2 is recompozíciót vált ki (új ref)
}

Primitív State: MutableIntState, MutableFloatState, MutableLongState

A primitív MutableIntState és társai — specializált interfészek, amelyek primitíveket tárolnak automatikus csomagolás (boxing) nélkül. A szokásos MutableState<Int> Integer-ként tárolja az Int-et, ami minden íráskor egy objektumot hoz létre a kupacon. A MutableIntState int-et (primitívet) tárol, teljesen kiküszöbölve a csomagolás többletterhelését. Ez különösen fontos nagy frekvenciájú frissítéseknél — számlálók, görgetési pozíciók, animációs értékek.

mutableIntStateOf(), mutableFloatStateOf(), mutableLongStateOf() — függvények, amelyek primitív MutableState-et hoznak létre. Az interfészek neve MutableIntState, MutableFloatState, MutableLongState. Ezek kiterjesztik a MutableState<Int>, MutableState<Float> és MutableState<Long> típusokat, hozzáadva az intValue tulajdonságot gyors primitív eléréshez. Belső megvalósításukban AtomicInteger-t használnak lock-free olvasáshoz/íráshoz.

Alkalmazás: számlálók (Int), görgetési pozíciók (Float offset), időbélyegek (Long). A mindennapi forgatókönyvek többségében a teljesítménykülönbség észrevehetetlen, de a LazyList-ben több ezer elemmel és átmeneti animációkkal a primitív State érzékelhető javulást nyújt. A Google a tipikus forgatókönyvekhez a primitív State használatát ajánlja az univerzális mutableStateOf helyett.

kotlin
@Composable
fun ScrollCounter() {
    // Rossz: csomagolás minden frissítéskor
    var badCount by remember { mutableStateOf(0) }

    // Jó: nincs csomagolás, primitív tárolás
    var goodCount by remember { mutableIntStateOf(0) }

    // Használat azonos
    Button(onClick = { goodCount++ }) {
        Text("Count: $goodCount")
    }
}

Gyakorlati példák a MutableState használatára

Vizsgáljuk meg a TodoList komponenst, ahol a MutableState két formában használatos: külön változókként a beviteli állapothoz és SnapshotStateList-ként a dinamikus feladatlistához. Mindkettő delegációt használ a kód tömörségéhez.

kotlin
data class TodoItem(val id: Int, val text: String, val isDone: Boolean = false)

@Composable
fun TodoScreen() {
    var inputText by remember { mutableStateOf("") }
    val items = remember { mutableStateListOf() }

    Column(modifier = Modifier.padding(16.dp)) {
        Row {
            TextField(
                value = inputText,
                onValueChange = { inputText = it }
            )
            Button(onClick = {
                if (inputText.isNotBlank()) {
                    items.add(TodoItem(items.size, inputText))
                    inputText = ""
                }
            }) { Text("Hozzáadás") }
        }

        LazyColumn {
            items(items) { item ->
                Row(modifier = Modifier.fillMaxWidth().clickable {
                    val idx = items.indexOf(item)
                    items[idx] = item.copy(isDone = !item.isDone)
                }) {
                    Checkbox(checked = item.isDone, onCheckedChange = null)
                    Text(item.text)
                }
            }
        }
    }
}

mutableStateListOf létrehoz egy SnapshotStateList-et — egy módosítható listát, amely követi az egyes elemek változásait. Az items.add() és items[n] = newValue hívásakor a Compose érzékeli a mutációt, és csak azokat a LazyColumn elemeket komponálja újra, amelyek megváltoztak. Az inputText — egy szokásos MutableState<String>. A két MutableState típus (egyedi és gyűjtemény) kombinációja — tipikus minta űrlapokkal és listákkal rendelkező képernyőkhöz.

Gyakran Ismételt Kérdések

Használható-e MutableState remember nélkül?

A MutableState remember nélkül minden recompozíciókor újra létrejön. Minden új mutableStateOf hívás új objektumot hoz létre, a régi érték elvész. Mindig használjon remember-t az State megőrzéséhez a recompozíciók között, kivéve ha az State a Composable-en kívül jön létre (pl. ViewModel-ben).

Hogyan alakítsuk át a MutableState-et közönséges változóvá?

Olvassa be a .value-t egyszer a snapshot-on kívül a snapshot { } segítségével. De ez kikapcsolja a reaktivitást — a változások többé nem váltanak ki recompozíciót. Egyszeri olvasáshoz feliratkozás nélkül használja a currentValue()-t a snapshot-on belül olvasás nélkül.

Melyik gyorsabb: mutableStateOf vagy mutableIntStateOf?

A mutableIntStateOf gyorsabb, mert nem igényli az int Integer-be csomagolását. Több ezer frissítés másodpercenként (animáció, görgetés) esetén a különbség elérheti a 30-50%-ot a foglalási időben. Ritka frissítéseknél (kattintások, szövegbevitel) a különbség elhanyagolható.

Használható-e MutableState Composable függvény argumentumaként?

Használható, de nem ajánlott. MutableState helyett adjon át State-t (csak olvasható) + onValueChange lambdát. Ez megvalósítja az State Hoisting mintát és újra felhasználhatóvá teszi a komponenst. A MutableState-et elfogadó komponensek megsértik az egyirányú adatáramlást.

Hogyan hozzunk létre egyedi MutableState megvalósítást?

Valósítsa meg a MutableState interfészt és biztosítson override var value-t getterrel és setterrel. A setterben hozzáadhat érvényesítést vagy naplózást. A Compose Runtime-tal való visszafelé kompatibilitás érdekében csomagolja a megvalósítást snapshotFlow-ba, vagy használja a snapshotIncrement-et.

Összefoglalás

  • MutableState — alapvető interfész a változtatható megfigyelhető állapothoz a Compose-ban
  • State — csak olvasható verzió adatok továbbításához módosítási jog nélkül
  • SnapshotMutationPolicy kezeli a recompozíció elindításának feltételeit változáskor
  • Primitív State (MutableIntState stb.) megszünteti az automatikus csomagolás többletterhelését
  • SnapshotStateList és SnapshotStateMap követi a gyűjtemények belső változásait
  • Recompozíció automatikusan elindul a value setter minden meghívásakor
  • Ajánlás: használja a MutableState-et az állapot birtoklásához és a State-et a lefelé továbbításhoz

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