Android从传统的XML转到Compose的变化:mutableStateOf、MutableStateFlow;有的使用by有的使用by remember

目录

  1. 前言

  2. XML和Compose分别用的数据观察对象(mutableStateOf和LiveData)

  3. mutableStateOf和MutableStateFlow的区别

    3.1 快速入门(基本类型和不可变数据类的区别)

  4. 何时选择哪种工具?

  5. 为什么有的使用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?

首先,我们要知道

  1. 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++ }) // 直接修改值
  1. 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二者通常一起使用,实现以下目标:

  1. ​状态持久化​ :remember 确保状态在重组中保留。
  2. ​语法简化​ :by 避免手动操作 .value。
相关推荐
熊猫钓鱼>_>10 小时前
Kotlin Multiplatform for OpenHarmony 实战:为 Reaktive 实现响应式原语适配(完整版 · 含摘要目录与技术图表)
华为·kotlin·大模型·ai编程·harmonyos·适配·reaktive
五彩小白11 小时前
自回归和上下文
android
事圆则缓13 小时前
Android 多渠道打包实战:Product Flavors、签名、AAB 与发包校验
android
树下困觉眯16 小时前
View绘制流程学习--获取WindowManager的三种方式
android·系统架构·view design
vilya17 小时前
我怎么给手机 GUI Agent 做双通道感知:无障碍树为主,投屏像素兜底
android·人工智能
李游Leo17 小时前
HarmonyOS 7 InsightBoard 多形态适配实录 06:ArkUI × 多设备回归:布局抖动、监听释放与全形态验收【鸿蒙心迹】
回归·kotlin·harmonyos
点燃大海17 小时前
手机给平板当键盘?
kotlin
李子红了时18 小时前
阿狸舞台APP(安卓端手机EncFS解密+压缩包解压+视频播放)
android·音视频
事圆则缓18 小时前
Android 使用 Jenkins 实现 CI/CD,并用 SonarQube 建立质量门禁
android·ci/cd·jenkins
其实防守也摸鱼18 小时前
DeepSeek Harness 开源贡献手记:从 Issue 到 Merge 的完整旅程
android·数据库·学习·ai·oracle·自动化