Release构建下Jetpack Compose Text组件停止更新问题求助
Release构建下Compose Text组件停止更新的问题分析与解决
问题重现
Debug构建中,显示通话时长的Text组件能随callDuration正常每秒更新;但在Release构建里,时长更新到"00:02"后就卡住不再变化,实际callDuration参数仍在每秒更新。只有触摸或交互屏幕其他组件时,时长才会恢复更新。
相关代码如下:
调用方式
CallStatusInfo( modifier = Modifier .align(Alignment.Center) .padding(vertical = 8.dp), callStatus = viewModel.callStatus, callDuration = viewModel.callDuration )
Composable实现
@OptIn(ExperimentalFoundationApi::class) @Composable fun CallStatusInfo(modifier: Modifier = Modifier, callStatus: String, callDuration: String?) { Text( modifier = modifier.basicMarquee(), text = when(callStatus){ "ringing" -> "ringing" "connected" -> callDuration ?: "" else -> "" }, style = MaterialTheme.typography.body2, fontWeight = FontWeight.W600, maxLines = 1 ) }
ViewModel核心逻辑
var callDuration by mutableStateOf<String?>(null) private set var callStatus by mutableStateOf<String>("connected") private set private var startTime = Instant.now() init { viewModelScope.launch { while (true) { val timeElapsed = System.currentTimeMillis() - startTime.toEpochMilli() val duration = formatTimeToDuration(timeElapsed) callDuration = duration delay(1.seconds) } } }
问题原因
这是basicMarquee这个实验性修饰符在Release构建下的重组优化bug导致的:
- Compose的Release构建会做激进的重组跳过优化,只有当Composable的输入状态被正确标记为"需要重组"时,UI才会更新。
basicMarquee内部的状态跟踪存在缺陷,当Text内容(由callDuration驱动)持续变化时,Marquee的动画状态没有触发Compose的重组检测,导致Compose认为UI无需更新,直到有外部交互(比如触摸)触发全局重组,才会刷新显示。
解决方案
方案1:替换实验性的basicMarquee
如果不需要跑马灯效果,直接移除basicMarquee()修饰符:
Text( modifier = modifier, // 移除basicMarquee text = when(callStatus){ "ringing" -> "ringing" "connected" -> callDuration ?: "" else -> "" }, style = MaterialTheme.typography.body2, fontWeight = FontWeight.W600, maxLines = 1 )
如果需要跑马灯,使用稳定的第三方实现或者自定义Marquee组件,避免依赖实验性API。
方案2:强制触发重组
通过给Text添加key修饰符,强制Compose在callDuration变化时重组Text:
Text( modifier = modifier.basicMarquee(), text = when(callStatus){ "ringing" -> "ringing" "connected" -> callDuration ?: "" else -> "" }, style = MaterialTheme.typography.body2, fontWeight = FontWeight.W600, maxLines = 1, key = callDuration // 添加key,强制重组 )
方案3:使用derivedStateOf计算文本内容
将文本内容用derivedStateOf包装,确保Compose能正确跟踪状态变化:
@OptIn(ExperimentalFoundationApi::class) @Composable fun CallStatusInfo(modifier: Modifier = Modifier, callStatus: String, callDuration: String?) { val displayText by remember { derivedStateOf { when(callStatus){ "ringing" -> "ringing" "connected" -> callDuration ?: "" else -> "" } } } Text( modifier = modifier.basicMarquee(), text = displayText, style = MaterialTheme.typography.body2, fontWeight = FontWeight.W600, maxLines = 1 ) }
临时方案的原理说明
你用的临时方案之所以有效,是因为:
var duration by remember { mutableStateOf("") } LaunchedEffect(key1 = callDuration) { duration = callDuration ?: "" }
LaunchedEffect会监听callDuration的变化,每次变化都会更新本地的mutableState变量duration。- 本地
mutableState的变化会被Compose正确检测到,强制触发Text组件的重组,绕过了basicMarquee带来的重组抑制问题。但这只是绕开bug的临时手段,不是根本解决方式。
内容的提问来源于stack exchange,提问作者Pravesh Singh
相关产品推荐
相关产品推荐

