文章目录
- Compose 状态管理
- 概述
- 使用
- MutableState
- remember
- StatelessComposable & StatefulComposable
- 状态提升
- rememberSaveable
- 支持parceable
- 不支持parceable
- 使用ViewModel
- ViewModelProvider.Factory
- 使用Flow
Compose 状态管理
概述
当应用程序的状态发生变化时,Compose会进行重组,重组过程中会运行可能已更改的可组合项以响应状态变化,然后Compose会更新组合以反映所有更改。这就是Compose的工作流程。
使用
MutableState
val counter: MutableState<Int> = mutableStateOf(0)
这里的 counter 是一个MutableState<Int>
类型,可以使用 .value
进行读写。
// 解构
val (counter: Int, setCounter: (Int) -> Unit) = mutableStateOf(0)
这里的 counter 是一个 Int 类型的数据,其他地方可以直接访问,需要更新 counter 时可以使用 setCounter(xx) 完成。
// 属性代理
val counter: Int by mutableStateOf(0)
这里的 counter 的读写会通过 getValue 和 setValue这 两个运算符的重写最终代理为对 value 的操作,通过 by 关键字,可以像访问一个普通的Int变量一样对状态进行读写。
remember
- mutableStateOf() 函数会创建一个 MutableState 类型的对象,MutableState 是可观察类型,在值发生变化的情况下系统会安排重组持有该值的可组合函数。
- remember() 在Composable首次执行时,remember中计算得到的数据会自动缓存,当Composable重组再次执行到remember处会返回之前已缓存的数据,无须重新计算。
@Preview
@Composable
fun Test3() {val index = remember {mutableStateOf(10)}Column(modifier = Modifier.fillMaxSize()) {Button(onClick = {index.value++Log.e("TAG", "点击了 :${index.value}")}) {Text("Click")}Text("${index.value}", fontSize = 30.sp)}
}
说明:使用 remember { mutableStateOf() }
记录状态,当 index 发生变化时,Text显示的值也会跟着发生变化。
StatelessComposable & StatefulComposable
StatelessComposable 不管理任何状态,它的输出仅取决于输入参数。它是无状态的,每次调用都会重新计算输出,并且不会记住之前的状态。
例如,一个简单的文本显示组件可以是一个 StatelessComposable
:
@Composable
fun MyTextComponent(text: String) { // StatelessComposableText(text = text)
}
说明:MyTextComponent
是一个 StatelessComposable
,它接受一个字符串参数 text
,并将其显示为文本。
StatefulComposable 会管理状态,内部持有或访问状态,并根据状态的变化来更新输出。
例如,一个计数器组件可以是一个 StatefulComposable
:
@Composable
fun MyCounterComponent() { // StatefulComposablevar count by remember { mutableStateOf(0) }Button(onClick = { count++ }) {Text(text = "计数器:$count")}
}
说明:MyCounterComponent
是一个 StatefulComposable
,它内部使用 mutableStateOf
来管理一个整数状态 count
。当点击按钮时,count
会增加 1,并更新计数器的显示。
StatelessComposable
更简单、高效,适用于不需要管理状态的组件,Stateless是一个“纯函数”,参数是变化的唯一来源,参数不变UI就不会变化。因此Compose编译器针对其进行了优化;而 StatefulComposable
更灵活、强大,适用于需要管理状态的组件。StatelesComposable 的重组只能来自上层 Composable 的调用,而 StatefulComposable 的重组来自其依赖状态的变化。
状态提升
状态提升也就是将 Statefule 改造为 Stateless,Stateless 由于不耦合任何业务逻辑,所以功能更加纯粹,相对于 Stateful 的可复用性更好,对测试也更加友好。
// StatefulComposable
@Composable
fun CounterScreen() {var counter by remember { mutableStateOf(0) }CounterComponent(counter = counter,onIncrement = { counter++ },onDecrement = {if (counter > 0) {counter--}})
}// StatelessComposable
@Composable
fun CounterComponent(counter: Int,onIncrement: () -> Unit,onDecrement: () -> Unit,
) {Column(modifier = Modifier.padding(16.dp)) {Text("Counter:", modifier = Modifier.fillMaxWidth(), textAlign = TextAlign.Center)Text("$counter",modifier = Modifier.fillMaxWidth(),textAlign = TextAlign.Center,fontWeight = FontWeight.Bold)Row {Button(onClick = { onDecrement() }, modifier = Modifier.weight(1F)) {Text("-")}Button(onClick = { onIncrement() }, modifier = Modifier.weight(1F)) {Text("+")}}}
}
说明:CounterComponent经状态上提后,职责更加单一。
rememberSaveable
rememberSaveable() 函数可以保证ConfigurationChanged事件发生时(如屏幕旋转等)保存状态,数据会随onSaveInstanceState进行保存。
并在进程或者Activity重建时根据key恢复到对应的Composable中,这个key就是Composable在编译期被确定的唯一标识。因此当用户手动退出应用时,rememberSavable中的数据才会被清空。
rememberSavable实现原理实际上就是将数据以Bundle的形式保存,所以凡是Bundle支持的基本数据类型都可以自动保存。对于一个对象类型,则可以通过添加@Parcelize变为一个Parcelable对象进行保存。
支持parceable
添加kotlin-parcelize插件:
plugins {id 'kotlin-parcelize'
}
使用:
// 定义数据类
@Parcelize
data class Person(val name: String, val age: Int) : Parcelable// 使用:
@Composable
fun PersonScreen() {var person by rememberSaveable { mutableStateOf(Person("小白", 18)) }Button(onClick = { person = Person("小黑", 28) }) {Text(person.toString())}
}
不支持parceable
有的数据结构可能无法添加Parcelable接口,比如定义在三方库的类等,此时可以通过自定义Saver为其实现保存和恢复的逻辑。只需要在调用rememberSavable时传入此Saver。
// 定义数据类
data class People(val name: String, val age: Int)// 定义Saver
object PersonSaver : Saver<People, Bundle> {override fun restore(value: Bundle): People {val name = value.getString("name") ?: ""val age = value.getInt("age")return People(name, age)}override fun SaverScope.save(value: People): Bundle {return Bundle().apply {putString("name", value.name)putInt("age", value.age)}}
}// 使用:
@Composable
fun PeopleScreen() {var people by rememberSaveable(stateSaver = PersonSaver) { mutableStateOf(People("小红", 18)) }Button(onClick = { people = People("小绿", 28) }) {Text(people.toString())}
}
支持MapSaver:
MapSaver将对象转换为 Map<String, Any>
的结构进行保存。
data class City(val name: String, val country: String)// 定义MapSaver
val CitySaver = run {val nameKey = "name"val countryKey = "country"mapSaver(save = { mapOf(nameKey to it.name, countryKey to it.country) },restore = { City(it[nameKey] as String, it[countryKey] as String) })
}@Composable
fun CityScreen() {var city by rememberSaveable(stateSaver = CitySaver) { mutableStateOf(City("北京", "中国")) }Button(onClick = { city = City("东京", "日本") }) {Text(city.toString())}
}
支持ListSaver:
ListSaver则是将对象转换为 List<Any>
的数据结构进行保存。
val CitySaver2 = listSaver<City, Any>(save = { listOf(it.name, it.country) },restore = { City(it[0] as String, it[1] as String) }
)
使用ViewModel
在 Compose 中也可以使用 ViewModel 缓存状态,通过 LiveData 或 Flow 监听变化。
添加依赖:
implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.5.1'
implementation "androidx.compose.runtime:runtime-livedata:$compose_version"
使用:
class CountViewModel : ViewModel() {private val _count = MutableLiveData(10)val count get() = _countfun onCountChanged(count: Int) {_count.postValue(count)}
}
@Composable
fun MyCount() {val viewModel: CountViewModel = viewModel()val count by viewModel.count.observeAsState(0) // 语法糖Column(modifier = Modifier.fillMaxSize(),verticalArrangement = Arrangement.Center,horizontalAlignment = Alignment.CenterHorizontally) {Text("$count")Button(onClick = { viewModel.onCountChanged(count + 2) }) {Text("点击")}}
}
说明:通过 observeAsState() 函数观察 LiveData 对象,当 LiveData 发生变化时,该对象都会更新。
ViewModelProvider.Factory
class CountViewModel(defaultCount: Int) : ViewModel() {private val _count = MutableLiveData(defaultCount)val count get() = _countfun onCountChanged(count: Int) {_count.postValue(count)}
}class CountViewModelFactory(private val defaultCount: Int) : ViewModelProvider.Factory {override fun <T : ViewModel> create(modelClass: Class<T>): T {return CountViewModel(defaultCount) as T}
}
@Composable
fun MyCount() {val viewModel: CountViewModel = viewModel(factory = CountViewModelFactory(100))val count by viewModel.count.observeAsState(0)Column(modifier = Modifier.fillMaxSize(),verticalArrangement = Arrangement.Center,horizontalAlignment = Alignment.CenterHorizontally) {Text("$count")Button(onClick = {viewModel.onCountChanged( count+ 2) }) {Text("点击")}}
}
使用Flow
class CountViewModel : ViewModel() {private val _countFlow = MutableStateFlow(10)val countFlow get() = _countFlow.asStateFlow()fun onCountChanged(count: Int) {_countFlow.value = count}
}
@Composable
fun MyCount() {val viewModel: CountViewModel = viewModel()val count: Int by viewModel.countFlow.collectAsState(0) // 语法糖Column(modifier = Modifier.fillMaxSize(),verticalArrangement = Arrangement.Center,horizontalAlignment = Alignment.CenterHorizontally) {Text("$count")Button(onClick = {viewModel.onCountChanged(count + 2)}) {Text("点击")}}
}