MutableState — ay isang interface sa Jetpack Compose na kumakatawan sa isang lalagyan para sa nababagong napagmamasdang halaga. Ito ang batayan ng reaktibong sistema ng Compose: sa bawat oras na ang halaga ng MutableState ay nagbabago sa pamamagitan ng setter, ang Compose Runtime ay nag-aabiso sa lahat ng nagbabasang bahagi at nagpapasimula ng recomposition. Ayon sa Google Android Developers, 2026, ang pag-unawa sa MutableState ay kinakailangan para sa tamang pagtatrabaho sa estado sa deklaratibong UI.
Mga pangunahing punto
MutableState — ay isang interface mula sa pakete na androidx.compose.runtime na nagdedeklara ng isang pag-aari: override var value: T. Ang getter ay nagbabalik ng kasalukuyang halaga, ang setter ay nagsusulat ng bago at nag-aabiso sa Compose Runtime tungkol sa pagbabago. Ang interface ay nagmamana mula sa State<T>, kung saan ang value ay nababasa lamang. Ang ganitong dalawang-antas na arkitektura ay nagpapahintulot ng paghihiwalay ng access: ang bahaging kailangan lamang basahin ang halaga ay tumatanggap ng State<T>, at ang bahaging nagmamay-ari — MutableState<T>.
Ang default na implementasyon ng MutableState ay ang panloob na klase na SnapshotMutableStateImpl, na gumagamit ng mekanismo ng snapshot para sa pagsubaybay ng mga pagbabago. Kapag ang setter ng value ay tinawag, ang kasalukuyang snapshot ay nagtatala ng pagsulat at minamarkahan ang lahat ng rehistradong ObservedScope (mga lugar ng pagmamasid) bilang hindi wasto. Ang mga lugar na ito (karaniwang mga Composable function) ay muling bubuuin sa susunod na frame. Ang buong proseso ay nagaganap nang sabay-sabay at walang pagharang salamat sa Lock-free snapshot na arkitektura.
State vs MutableState: State — ay isang interface na nababasa lamang, ginagamit para sa pampublikong API ng mga bahagi. Kapag idineklara mo ang isang parameter ng Composable function bilang State<Int>, ginagarantiya mo na ang bahagi ay maaaring magbasa ngunit hindi magbago ng estado. Ang MutableState ay ginagamit sa loob ng bahaging nagmamay-ari. Ang ganitong paghihiwalay — isa sa mga pangunahing kasanayan ng Compose — ay pumipigil sa hindi awtorisadong mga pagbabago.
Ang herarkiya ng mga interface ng estado sa Compose ay may ilang antas. Sa tuktok — State<T> na may value na nababasa lamang. Sa ibaba — MutableState<T> na may value na nababasa at nasusulat. Susunod ay ang mga espesyalisadong primitibong bersyon: MutableIntState, MutableFloatState, MutableLongState, MutableBooleanState at iba pa, na umiiwas sa auto-boxing ng mga primitive.
MutableDoubleState at MutableLongState — hindi gaanong karaniwan ngunit umiiral na mga uri. Mga interface ng koleksyon: MutableListState — para sa pagsubaybay ng mga pagbabago sa loob ng listahan, MutableStateMap — para sa mga mapa. Ang bawat isa sa mga interface na ito ay na-optimize para sa isang partikular na senaryo at pinapalawak ang batayang MutableState ng mga karagdagang pamamaraan para sa pagtatrabaho sa koleksyon.
SnapshotStateList at SnapshotStateMap — ay mga implementasyon ng nababagong listahan at mapa na katugma sa mga snapshot. Pinapayagan nila ang pagsubaybay hindi lamang ng pagpapalit ng halaga, kundi pati na rin ng mga panloob na pagbabago: pagdagdag ng elemento sa listahan, pagtanggal, pagbabago ng umiiral na elemento. Para sa mga ganitong istraktura, ang mutableStateListOf() at mutableStateMapOf() ay lumilikha ng kaukulang napagmamasdang mga koleksyon.
| Interface | Layunin | Paraan ng paglikha |
|---|---|---|
| State<T> | Lalagyan na nababasa lamang | — |
| MutableState<T> | Lalagyan na nababasa at nasusulat | mutableStateOf() |
| MutableIntState | Primitibong Int walang boxing | mutableIntStateOf() |
| MutableFloatState | Primitibong Float walang boxing | mutableFloatStateOf() |
| SnapshotStateList | Napagmamasdang listahan | mutableStateListOf() |
| SnapshotStateMap | Napagmamasdang mapa | mutableStateMapOf() |
SnapshotMutationPolicy — ay isang interface na tumutukoy kung kailan itinuturing na makabuluhan ang pagbabago ng MutableState. Ang mutableStateOf ay tumatanggap ng policy bilang pangalawang argumento. Mga karaniwang implementasyon: structuralEquality() (equals), referentialEquality() (===), neverEqualPolicy() (laging itinuturing na makabuluhan ang pagbabago). Para sa sariling lohika, maaari mong ipatupad ang iyong sariling policy.
structuralEquality() — default na pag-uugali. Inihahambing ng Compose ang bagong halaga sa luma sa pamamagitan ng equals(). Kung ang resulta ay true — ang recomposition ay HINDI napapasimulan. Ito ay maginhawa para sa mga primitive at data class, kung saan ang dalawang instance na may parehong mga patlang ay itinuturing na pantay. Problema: kung ang data class ay naglalaman ng List, ang equals() ay nagsasagawa ng malalim na paghahambing, na maaaring magastos para sa malalaking listahan.
referentialEquality() — inihahambing ang mga reference sa pamamagitan ng ===. Ang recomposition ay napapasimulan lamang kapag nagtalaga ng ibang bagay, kahit na ang nilalaman ay magkapareho. Ito ay pinakamainam para sa hindi nababagong data class, kung saan ang bawat bagong instance ay garantisadong nangangahulugan ng pagbabago. neverEqualPolicy() — laging itinuturing na makabuluhan ang pagbabago nang hindi nagsasagawa ng paghahambing. Kapaki-pakinabang kapag ang setter ay bihirang tinatawag at hindi kailangang gumugol ng oras sa equals.
// Paghahambing ng patakaran sa praktika
data class User(val name: String, val age: Int)
@Composable
fun UserProfile() {
// structuralEquality: recomposition LAMANG kung nagbago ang data
var user1 by remember {
mutableStateOf(User("Alice", 30))
}
// referentialEquality: recomposition sa BAWAT pagtatalaga
var user2 by remember {
mutableStateOf(User("Bob", 25),
SnapshotMutationPolicy.referentialEquality())
}
// user1: copy() na may parehong mga patlang ay HINDI nagpapasimula ng recomposition
// user2: kahit user2.copy() == user2 ay nagpapasimula ng recomposition (bagong ref)
}
Primitibong MutableIntState at mga katulad — ay mga espesyalisadong interface na nag-iimbak ng mga primitive nang walang auto-boxing. Ang karaniwang MutableState<Int> ay nag-iimbak ng Int bilang Integer, na sa bawat pagsulat ay lumilikha ng bagay sa heap. Ang MutableIntState ay nag-iimbak ng int (primitive), ganap na inaalis ang overhead ng pagbabalot. Ito ay lalong mahalaga sa mga update na may mataas na dalas — mga tagabilang, posisyon ng scroll, mga halaga ng animasyon.
mutableIntStateOf(), mutableFloatStateOf(), mutableLongStateOf() — mga function na lumilikha ng primitibong MutableState. Ang mga interface ay pinangalanang MutableIntState, MutableFloatState, MutableLongState. Pinapalawak nila ang MutableState<Int>, MutableState<Float> at MutableState<Long> ayon sa pagkakabanggit, nagdaragdag ng pag-aari na intValue bilang mabilis na access sa primitive. Sa kanilang panloob na implementasyon, ginagamit ang AtomicInteger para sa lock-free na pagbasa/pagsulat.
Paggamit: mga tagabilang (Int), mga posisyon ng scroll (Float offset), mga timestamp (Long). Sa karamihan ng pang-araw-araw na senaryo, ang pagkakaiba sa pagganap ay hindi napapansin, ngunit sa LazyList na may libu-libong elemento at animasyon ng paglipat, ang primitibong State ay nagbibigay ng kapansin-pansing pagtaas. Inirerekomenda ng Google ang paggamit ng primitibong State para sa karaniwang mga senaryo sa halip ng unibersal na mutableStateOf.
@Composable
fun ScrollCounter() {
// Mali: pagbabalot sa bawat update
var badCount by remember { mutableStateOf(0) }
// Mabuti: walang pagbabalot, primitibong imbakan
var goodCount by remember { mutableIntStateOf(0) }
// Ang paggamit ay magkapareho
Button(onClick = { goodCount++ }) {
Text("Count: $goodCount")
}
}
Tingnan natin ang bahaging TodoList, kung saan ginagamit ang MutableState sa dalawang anyo: bilang hiwalay na mga variable para sa estado ng input at bilang SnapshotStateList para sa dinamikong listahan ng mga gawain. Pareho silang gumagamit ng delegasyon para sa pagkaikli ng code.
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("Idagdag") }
}
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 ay lumilikha ng SnapshotStateList — isang nababagong listahan na sumusubaybay sa mga pagbabago ng indibidwal na mga elemento. Kapag tinawag ang items.add() at items[n] = newValue, nakikita ng Compose ang mutasyon at nire-recompose lamang ang mga elementong LazyColumn na nagbago. inputText — isang karaniwang MutableState<String>. Ang kombinasyon ng dalawang uri ng MutableState (iisa at koleksyon) — isang tipikal na pattern para sa mga screen na may mga form at listahan.
Mga Madalas Itanong
Ang MutableState na walang remember ay muling lilikhain sa bawat recomposition. Bawat bagong tawag sa mutableStateOf ay lumilikha ng bagong bagay, at ang lumang halaga ay nawawala. Palaging gamitin ang remember para mapanatili ang State sa pagitan ng mga recomposition, maliban kung ang State ay nilikha sa labas ng Composable (halimbawa, sa ViewModel).
Basahin ang .value nang isang beses sa labas ng snapshot sa pamamagitan ng snapshot { }. Ngunit ito ay nag-aalis ng reaktibidad — ang mga pagbabago ay hindi na magdudulot ng recomposition. Para sa isang beses na pagbasa nang walang subscription, gamitin ang currentValue() sa loob ng snapshot nang hindi nagbabasa.
Ang mutableIntStateOf ay mas mabilis dahil hindi ito nangangailangan ng pagbabalot ng int sa Integer. Sa libu-libong update bawat segundo (animasyon, scroll) ang pagkakaiba ay maaaring umabot ng 30-50% ng oras ng alokasyon. Sa bihirang mga update (mga click, pag-input ng teksto) ang pagkakaiba ay hindi gaanong mahalaga.
Maaari, ngunit hindi inirerekomenda. Sa halip ng MutableState, ipasa ang State (nababasa lamang) + lambda na onValueChange. Ito ay nagpapatupad ng pattern na State Hoisting at ginagawang magagamit muli ang bahagi. Ang mga bahaging tumatanggap ng MutableState ay lumalabag sa isang-direksyong daloy ng datos.
Ipatupad ang interface na MutableState at magbigay ng override var value na may getter at setter. Sa setter maaari kang magdagdag ng validation o pag-log. Para sa pabalik na pagkakatugma sa Compose Runtime, balutin ang iyong implementasyon sa snapshotFlow o gamitin ang snapshotIncrement.
Buod
Gagawa kami ng mobile application na turnkey
Gumagawa ang IT Sectr ng mga iOS at Android application para sa mga startup at negosyo mula noong 2017. Magpapayo kami sa iyo at magmumungkahi ng pinakamahusay na solusyon.
Basahin din