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 — 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.
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ész | Rendeltetés | Létrehozási metódus |
|---|---|---|
| State<T> | Csak olvasható tároló | — |
| MutableState<T> | Olvasható-írható tároló | mutableStateOf() |
| MutableIntState | Primitív Int boxing nélkül | mutableIntStateOf() |
| MutableFloatState | Primitív Float boxing nélkül | mutableFloatStateOf() |
| SnapshotStateList | Megfigyelhető lista | mutableStateListOf() |
| SnapshotStateMap | Megfigyelhető térkép | mutableStateMapOf() |
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.
// 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)
}
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.
@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")
}
}
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.
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
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).
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.
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ó, 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.
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
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