📐 可组合函数 · 核心三章

@Composable · 规则 · 重组

① @Composable 注解

@Composable 是 Jetpack Compose 的“魔法标记”。它告诉编译器:这个函数会发射 UI 或 描述界面结构。没有它,Compose 无法识别你的可组合项。

✅ 基本用法
🔹 函数名大写(惯例)
🔸 无返回值 (Unit)
@Composable
fun Greeting(name: String) {
    Text(text = "你好, $name!")
}

注解让 Compose 编译器能够追踪函数调用,并为后续的重组提供插桩。

② 可组合函数的规则

Compose 运行时对可组合函数有明确的约定,遵守规则才能保证正确、高效地渲染。

规则说明示例 / 反例
幂等 & 无副作用相同输入应产生相同输出,不修改外部状态❌ 在可组合内写 var count = 0
不依赖执行顺序可组合可能在任何顺序下执行(并行预览)✅ 只依赖参数和 Composable 作用域
快速 & 无阻塞不要执行耗时操作、网络请求或复杂计算✅ 使用 LaunchedEffect 处理副作用
读取状态需重组通过 State / MutableState 读取,触发重组✅ val count by remember { mutableStateOf(0) }
可组合函数可以多次执行重组时会重新调用,不要假设只执行一次✅ 用 remember 缓存昂贵对象

💡 黄金法则:可组合函数只描述 UI,所有“副作用”都交给 LaunchedEffect / DisposableEffect 等作用域。

③ 重组 (Recomposition) & 智能重组

重组:当状态变化时,Compose 重新执行受影响的可组合函数以更新 UI。
智能重组:Compose 编译器通过静态分析,只重组读取了变化状态的代码块,跳过未读取的部分,极大提升性能。

ViewModel State: count ⚡ 智能重组 跳过未读取状态 仅重组 Text(count) Text(count) Image(logo) 重组仅影响左边 Image 不重组 ✅

重组触发流程

  • 状态变化 → 例如 count++
  • 快照系统 记录变化,标记读取了该状态的作用域
  • Compose 调度器 在下一帧安排重组
  • 智能跳过 未读取变化状态的 lambda / 可组合

代码示例:重组对比

@Composable
fun CounterExample() {
    var count by remember { mutableStateOf(0) }
    val logo = remember { /* 昂贵计算 */ }

    Column {
        // 只有这个 Text 读取 count → 重组时仅它重新执行
        Text("点击次数: $count")  
        
        // 未读取 count → 智能重组跳过
        Image(bitmap = logo, contentDescription = "Logo")
        
        Button(onClick = { count++ }) {
            Text("增加")
        }
    }
}

🔍 点击按钮后,Text("点击次数: $count") 重组,Image 和 Button 的 content 不会重新执行,这就是智能重组的威力。

📘 ViewModel 集成 使用 viewModel() 配合 StateFlow / collectAsState() 实现稳定的状态管理,重组只发生在 collect 读取处。