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 条评论

发表回复

您的电子邮箱地址不会被公开。