Jetpack Compose函数为何在输入参数变化时被调用?源码依据是什么
Jetpack Compose 输入参数变化触发函数执行的原理
所有@Composable函数的执行都绑定了独立的重组作用域,输入参数变化触发函数重新执行的逻辑由「编译期插桩」和「运行时调度」两部分配合实现:
1. 编译期插桩生成参数比较逻辑
Compose 编译器插件会在编译阶段对所有带@Composable注解的函数做改造,自动插入参数校验、状态关联的逻辑:
- 每个Composable函数会被额外注入
$composer、$changed两个隐式参数 - 函数执行前会将当前输入参数和上一次缓存在
Composer节点中的参数快照做全等比较,判断参数是否发生变更 - 你可以通过查看编译后生成的Composable函数字节码反编译的代码,看到类似如下逻辑:
// 编译插桩后生成的示例伪代码 fun UserCard(name: String, age: Int, modifier: Modifier, $composer: Composer, $changed: Int) { $composer.startRestartGroup(12345) // 计算参数变更标记位 val dirtyFlags = $changed or if (name == $composer.nextCachedValue()) 0 else 0b01 or if (age == $composer.nextCachedValue()) 0 else 0b10 // 只要参数发生变更,或者作用域被标记为需要重组,就执行函数体 if (dirtyFlags != 0 || $composer.changed(12345)) { // 原始函数体逻辑 Column(modifier) { Text(text = name) Text(text = age.toString()) } } else { // 参数无变化,跳过执行,直接复用之前的布局节点 $composer.skipToGroupEnd() } // 注册重组回调,后续作用域失效时会触发当前函数重新执行 $composer.endRestartGroup()?.updateScope { nextComposer, nextChanged -> UserCard(name, age, modifier, nextComposer, nextChanged) } }
2. 运行时调度执行重组
当参数比较发现有变更时,对应的重组作用域会被标记为失效状态,Recomposer类作为Compose的重组调度核心,会在每一帧的绘制前回调中遍历所有失效的作用域,按照依赖顺序触发对应Composable函数的重新执行,这部分逻辑可以参考Recomposer#runRecomposeAndApplyChanges方法的实现。
注意:只有参数类型被标记为
@Stable时,编译器才会生成可靠的比较逻辑,非稳定类型的参数默认会被判定为每次都发生变更,可能触发不必要的重组。
内容的提问来源于stack exchange,提问作者vikingcheng
相关产品推荐
相关产品推荐

