https://developer.android.google.cn/develop/ui/compose/mental-model?hl=zh-cn
目录
💡 核心思想:声明性UI
在传统的命令式UI中,你需要手动获取视图对象并调用其方法(如setText())来更新界面。这不仅繁琐,还容易导致状态不一致。
Compose采用的是声明性范式。你只需要描述界面应该呈现的样子,它完全由状态驱动。
- UI = f(状态):界面是状态的一个函数。当状态变化时,Compose会智能地重新执行受影响的可组合函数,并自动更新界面。这个过程被称为重组。
🧱 基本构建单元:可组合函数
可组合函数是Compose中最基础的单元,它有以下几个重要特征:
@Composable注解:所有可组合函数都必须有此注解,以告知编译器将其转换为界面。- 接受数据,发出UI:函数接收数据作为参数,并通过调用其他可组合函数来生成界面。
- 无返回值:它不返回任何对象,而是描述了目标屏幕的状态。
- 快速、幂等、无副作用:这是可组合函数为了支持高效重组而必须遵循的重要属性。后面会详细解释。
🔄 动态内容与Kotlin的力量
因为可组合函数是用Kotlin编写的,所以它拥有了底层语言的全部灵活性。
- 你可以使用
if语句来决定是否显示某个元素。 - 你可以使用
for循环来动态生成列表。 - 你可以定义变量、调用辅助函数等。
这种灵活性是Compose的强大优势之一,让你可以更简洁、自然地构建动态界面。
⚙️ 核心机制:智能重组
重组是Compose响应状态变化、更新界面的核心机制。为了提高效率,Compose采用了一系列优化策略,理解这些对你写出正确、高效的代码至关重要。
- 跳过不必要的重组:Compose会尽力只重组那些输入参数已更改的部分。例如,在列表中,如果
header变了而names没变,LazyColumn的列表项就可能被跳过,不会重组。 - 乐观地取消和重启:重组是乐观操作,如果在新重组完成前参数又变了,Compose会取消当前重组并用新参数重新开始。
- 高频执行:可组合函数可能非常频繁地运行(例如动画的每一帧)。因此,函数内部绝不能执行耗时操作(如读取文件),否则会导致界面卡顿。
- 可能并行运行:为了优化性能,Compose可以在后台线程池中并行执行不同的可组合函数。
- 执行顺序不确定:同一层级下的可组合函数调用(如
StartScreen()和EndScreen()),其执行顺序是不保证的。它们不应相互依赖。
⚠️ 最重要的规则:无副作用
上述所有优化策略(跳过、取消、并行、顺序不定)共同指向了Compose编程中最重要的一条规则:可组合函数必须保持“无副作用”。
所谓副作用,是指对可组合函数作用域外部的可见状态所做的任何更改。例如:
- 写入共享对象的属性或全局变量。
- 更新
ViewModel中的可观察项。 - 写入
SharedPreferences或数据库。
违反这条规则会导致难以预测的bug,比如界面显示错误的状态,或是在重组被取消后依然错误地应用了某些操作。
✅ 最佳实践与模式
为了遵循上述规则,你需要采用新的编程模式:
-
将数据作为参数传入:不要从可组合函数内部读取
SharedPreferences或资源,而是将读取后的值作为参数传递进来。 -
通过回调触发操作:当用户交互需要改变状态时(如点击按钮),通过回调函数(如
onClick)将事件传递给ViewModel或其他逻辑层去处理状态更新。kotlin
// 正确做法:无副作用,仅依赖输入和回调 @Composable fun SharedPrefsToggle( value: Boolean, // 状态作为参数传入 onValueChanged: (Boolean) -> Unit // 操作通过回调传出 ) { Checkbox(checked = value, onCheckedChange = onValueChanged) }
📌 总结要点
| 核心概念 | 简要说明 |
|---|---|
| 声明性UI | UI是状态的函数,UI = f(状态)。 |
| 可组合函数 | 用@Composable标记,描述UI,无返回值。 |
| 智能重组 | 状态变化时,高效、智能地更新UI的机制。 |
| 无副作用 | 最重要的规则:不修改外部状态,不执行耗时操作。 |
| 状态提升 | 将状态移至可组合项外部,通过参数和回调传递。 |
0 条评论