Jetpack Compose中remember值重置及LaunchedEffect不触发问题
remember值重置与LaunchedEffect不触发问题解析
问题场景
在Composable中定义remember缓存值:
var imageWidth = remember { 0f }
仅以currentRotation为key的LaunchedEffect负责写入该值,预期仅旋转角度变化时重算图片宽度:
LaunchedEffect(key1 = currentRotation) { val ratio = imageBitmap.getRatio(currentRotation) imageWidth = // 具体宽度计算逻辑 }
实际表现:首次进入页面时LaunchedEffect正常触发,imageWidth赋值正确;点击页面部分按钮后,imageWidth意外重置为初始值0,且未触发重算。
问题1:remember缓存值被丢弃/重置的场景
- 可组合项完全从Composition树中移除:包括离屏销毁、条件渲染分支切换导致该节点不再被加入组合树,对应remember存储的实例会被直接回收,下次节点重新进入组合时,会重新执行remember初始化块返回初始值。
- remember传入的key发生变化:调用remember时如果传入了观察key,key变化时框架会判定旧值失效,重新执行初始化块生成新值。无参调用
remember { 0f }时,只要节点仍在组合树中,正常重组不会触发值重置。 - 可组合项的位置标识失效:组合树结构变更时(如LazyList item未设置稳定key、同位置Composable类型切换),框架会判定对应位置是全新节点,旧节点绑定的remember值会被直接丢弃。
- Composition上下文被完全销毁:如Activity/Fragment销毁、导航栈移除当前页面,整个Composition持有的所有remember值都会被回收。
- 状态未正确托管:如果remember存储的是普通值而非
MutableState,对变量的赋值不会写入remember的存储实例,下一次重组时变量会被重新赋值为remember中存储的初始值,表现为值被重置。
问题2:可组合项重建后LaunchedEffect未执行的原因
LaunchedEffect的重启规则是:仅当绑定的key与上一次组合时的key不相等,或者所在节点首次进入组合时,才会取消旧协程、重新执行块内逻辑。出现重建后不执行的常见原因如下:
- key值未发生变化:节点被移除重建时,如果获取到的
currentRotation值和该LaunchedEffect上一次启动时的key完全相等,LaunchedEffect不会重启。如果此时remember值已经被重置为初始0f,就会出现值为初始值但无重算逻辑触发的现象。 - 作用域不匹配:
imageWidth声明的Composable节点和LaunchedEffect所在的Composable节点不在同一层级。比如LaunchedEffect声明在未被销毁的父节点中,而imageWidth声明在反复被移除重建的子节点中,父节点的LaunchedEffect一直存活、key也未变化,自然不会重新触发写入,子节点重建后remember重置为0,就会读到错误值。 - key本身不是可观察状态:如果
currentRotation是未托管的普通变量,而非被mutableStateOf、StateFlow等可观察对象包裹的状态,LaunchedEffect无法感知到它的变化,只会在首次进入组合时取一次初始值启动协程,后续节点重建时如果值未变就不会重启。 - 代码本身的声明错误:如前文提到的,
imageWidth未用mutableStateOf包裹时,首次执行LaunchedEffect的赋值仅修改了当前重组帧的局部变量,根本没有持久化到remember存储中,后续任意重组都会把值拉回初始0f,看起来就像重置后没有触发重算——实际上LaunchedEffect本身就没有需要重启的理由,因为节点从未离开组合、key也没有变化。
内容的提问来源于stack exchange,提问作者TareK Khoury
相关产品推荐
相关产品推荐

