目录
-
前言
-
XML和Compose分别用的数据观察对象(mutableStateOf和LiveData)
-
mutableStateOf和MutableStateFlow的区别
3.1 快速入门(基本类型和不可变数据类的区别)
-
何时选择哪种工具?
-
为什么有的使用by,有是用=,有的使用by remember?
一、前言
总是被mutableStateOf、MutableStateFlow以及LiveData这些给搞混乱,这里我们就理清一下。
如果大家学过传统的XML方式编写界面,然后又学习了Compose,就会发现,他们观察数据变化的对象用的不同了,以前我们用的是LiveData,但换成Compose以后,变成mutableStateOf,但又发现,有些用的是MutableStateFlow,并且,有时候他们赋值的时候,使用by,或者使用by remember,赋值的时候,有时候用=,有时候用value。之前一直用的是MVVM,然后用了MVI,MVI应该用LiveData,还是mutableStateOf呢?总之呢,非常的混乱,这一篇文章呢,就是用来帮助梳理的。
二、XML和Compose分别用的数据观察对象
首先,我们要知道mutableStateOf
是专为 Jetpack Compose 设计的,通常与 Compose 搭配使用,而不是传统的 XML 布局。
mutableStateOf
是 Compose 声明式编程模型的核心工具,状态变化会自动触发 UI 重组,开发者无需手动更新视图(无需 findViewById
或 setText
)。
接下来我们通过代码例子来看看:
2.1 Compose + mutableStateOf(推荐)
kotlin
@Composable
fun Counter() {
var count by remember { mutableStateOf(0) }
Button(onClick = { count++ }) {
Text("Count: $count") // 自动更新
}
}
2.2 XML + LiveData(传统方式)
kotlin
// ViewModel
class CounterViewModel : ViewModel() {
private val _count = MutableLiveData(0)
val count: LiveData<Int> = _count
fun increment() {
_count.value = _count.value?.plus(1)
}
}
// Activity/Fragment
viewModel.count.observe(this) { value ->
binding.textView.text = "Count: $value" // 手动更新
}
其实这里就已经可以回答一个问题:之前一直用的是MVVM,然后用了MVI,MVI应该用LiveData,还是mutableStateOf呢?答案显而易见。
三、mutableStateOf和MutableStateFlow的区别
3.1 为什么会有两个
- mutableStateOf :Jetpack Compose 是声明式 UI 框架,需要轻量级、线程安全且与 UI 生命周期绑定的状态管理工具。响应式更新:状态变化时自动触发 UI 重组,无需手动通知。
MutableStateFlow
的出现原因
- mutableStateOf :Kotlin 协程需要一种支持多线程、可观察的数据流工具。跨组件通信:用于在 ViewModel、Repository 等非 UI 层之间共享状态,支持复杂的异步操作。
3.2 快速入门(基本数据类型)
(1)创建和初始化
kotlin
// 创建方式1:直接初始化
var count = mutableStateOf(0) // MutableState<Int>
// 创建方式2:配合 remember(在 Composable 中保持状态)
@Composable
fun Counter() {
var count by remember { mutableStateOf(0) } // 委托属性简化访问
}
kotlin
// 在 ViewModel 中创建
class MyViewModel : ViewModel() {
private val _count = MutableStateFlow(0) // 可变的 StateFlow
val count: StateFlow<Int> = _count.asStateFlow() // 对外暴露不可变版本
}
(2)修改值
csharp
// 直接修改 .value
count.value = 10
// 委托属性(更简洁)
var count by remember { mutableStateOf(0) }
count = 20
rust
// 直接赋值
_count.value = 30
// 原子性更新(避免多线程竞争)
_count.update { current -> current + 1 }
(3)观察值
kotlin
@Composable
fun DisplayCount() {
val count by viewModel.count.collectAsState() // 将 StateFlow 转为 State
Text(text = "Count: $count") // 自动重组 Compose自动观察
}
go
// 在 Activity/Fragment 中观察(通过协程收集)
viewModel.count.collectLatest { value ->
println("Current count: $value") // 处理数据流
}
3.3 快速入门(不可变数据类)
kotlin
// 定义不可变数据类
data class User(val name: String, val age: Int)
// 使用 mutableStateOf
var userState = mutableStateOf(User("Alice", 20))
userState.value = userState.value.copy(age = 21) // ✅ 生成新对象
// 使用 MutableStateFlow
private val _userFlow = MutableStateFlow(User("Bob", 25))
_userFlow.value = _userFlow.value.copy(age = 26) // ✅ 生成新对象
共同原则 :不可变数据必须通过 copy
生成新对象触发更新,否则不会触发重组。
四、 何时选择哪种工具?
4.1 实战:计数器(对比两种方式)
使用 mutableStateOf
(UI 层状态)
kotlin
@Composable
fun Counter() {
var count by remember { mutableStateOf(0) }
Button(onClick = { count++ }) {
Text("Count: $count")
}
}
使用 MutableStateFlow
(跨层状态)
kotlin
// ViewModel
class CounterViewModel : ViewModel() {
private val _count = MutableStateFlow(0)
val count: StateFlow<Int> = _count.asStateFlow()
fun increment() {
_count.update { it + 1 }
}
}
// Composable
@Composable
fun CounterScreen(viewModel: CounterViewModel) {
val count by viewModel.count.collectAsState()
Button(onClick = { viewModel.increment() }) {
Text("Count: $count")
}
}
场景 | 推荐工具 |
---|---|
Compose UI 组件的内部状态管理 | mutableStateOf |
跨组件/跨层(如 ViewModel 到 UI) | MutableStateFlow |
需要多线程安全 | MutableStateFlow |
简单 UI 状态(如按钮点击) | mutableStateOf |
复杂业务逻辑(如网络请求) | MutableStateFlow |
五、为什么有的使用by,有是用=,有的使用by remember?
首先,我们要知道
- by是什么?by是创建委托对象,在调用的时候才会赋值。 使用
by
委托(自动解包,隐藏.value。将属性的
getter和
setter自动映射到
MutableState.value`` )
ini
var isVisible by remember { mutableStateOf(false) }
// 直接赋值(等效于 isVisible.value = true)
isVisible = true
-------------------------------------------------------------
// 不使用 by(需手动操作 .value)
val count = remember { mutableStateOf(0) }
Text(text = "Count: ${count.value}")
Button(onClick = { count.value++ })
// 使用 by(自动委托 .value)
var count by remember { mutableStateOf(0) }
Text(text = "Count: $count") // 直接访问值
Button(onClick = { count++ }) // 直接修改值
- remember是什么?在 Composable 重组(重新绘制)时保持数据不被重置。 (避免因重组导致状态丢失)
kotlin
// 错误:每次重组都会重置 count 为 0
@Composable
fun Counter() {
var count = mutableStateOf(0) // ❌ 重组时会被重新初始化
Button(onClick = { count.value++ }) { Text("Count: ${count.value}") }
}
// 正确:使用 remember 保持状态
@Composable
fun Counter() {
var count by remember { mutableStateOf(0) } // ✅ 重组时保留之前的值
Button(onClick = { count++ }) { Text("Count: $count") }
}
remember
会将对象存储在 Composition 中,仅在首次调用时初始化,后续重组直接返回已存储的值。
by和remember二者通常一起使用,实现以下目标:
- 状态持久化 :
remember
确保状态在重组中保留。 - 语法简化 :
by
避免手动操作.value
。