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 重组,开发者无需手动更新视图(无需 findViewByIdsetText)。

接下来我们通过代码例子来看看:

​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。将属性的 gettersetter自动映射到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
相关推荐
QING61844 分钟前
Kotlin windowedSequence用法及代码示例
android·kotlin·源码阅读
QING6181 小时前
Kotlin MatchResult.Destructured用法及代码示例
android·kotlin·源码阅读
恋猫de小郭2 小时前
注意,暂时不要升级 MacOS ,Flutter/RN 等构建 ipa 可能会因 「ITMS-90048」This bundle is invalid 被拒绝
android·前端·flutter
每次的天空8 小时前
Android学习总结之算法篇五(字符串)
android·学习·算法
Gracker9 小时前
Android Weekly #202513
android
张拭心11 小时前
工作九年程序员的三月小结
android·前端
每次的天空11 小时前
Flutter学习总结之Android渲染对比
android·学习·flutter
鸿蒙布道师14 小时前
鸿蒙NEXT开发土司工具类(ArkTs)
android·ios·华为·harmonyos·arkts·鸿蒙系统·huawei
智想天开14 小时前
11.使用依赖注入容器实现松耦合
android
居然是阿宋14 小时前
Kotlin 集合函数:map 和 first 的使用场景
kotlin