You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Jetpack Compose进度条每秒更新时出现异常行为问题

问题原因

在Composable内部直接创建Handler并post Runnable的核心问题是:每次progress更新触发Composable重组时,都会重新生成新的Handler和Runnable实例,并且重复post任务。最终导致多个Runnable同时在主线程队列中执行,每秒会多次执行progress +=0.01f,进度增速自然远超预期。

而普通float变量的逻辑不在Composable上下文内,不会触发重组,因此始终只有一个Runnable循环执行,速度符合设定。


解决方法

方法1:使用LaunchedEffect(推荐)

LaunchedEffect是Compose专为协程异步任务设计的API,它会在Composable首次进入组合时启动协程,仅在指定key变化时才重启,从根源避免重复创建任务。

var progress by remember { mutableStateOf(0.00f) }
val animatedProgress by animateFloatAsState(
    targetValue = progress,
    animationSpec = ProgressIndicatorDefaults.ProgressAnimationSpec
)

// 传入Unit作为key,确保仅在首次组合时启动协程
LaunchedEffect(Unit) {
    // 可添加终止条件,比如进度到100%时停止
    while (progress < 1.0f) {
        delay(1000)
        progress += 0.01f
        println(progress)
    }
}

LinearProgressIndicator(progress = animatedProgress)

方法2:用remember保留Handler和Runnable实例

通过remember让Handler和Runnable在重组时保持唯一实例,避免重复post任务:

var progress by remember { mutableStateOf(0.00f) }
val animatedProgress by animateFloatAsState(
    targetValue = progress,
    animationSpec = ProgressIndicatorDefaults.ProgressAnimationSpec
)

// 用remember保存Handler,重组时复用同一个实例
val mainHandler = remember { Handler(Looper.getMainLooper()) }
// 用remember保存Runnable,确保不会重复创建
val updateRunnable = remember {
    object : Runnable {
        override fun run() {
            if (progress >= 1.0f) {
                mainHandler.removeCallbacks(this)
                return
            }
            progress += 0.01f
            println(progress)
            mainHandler.postDelayed(this, 1000)
        }
    }
}

// 用LaunchedEffect触发首次任务,避免重组时重复post
LaunchedEffect(Unit) {
    mainHandler.post(updateRunnable)
}

LinearProgressIndicator(progress = animatedProgress)

内容的提问来源于stack exchange,提问作者Pisoj

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 10:56:06