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

基于协程的CountDownTimer不符合预期:多次启动触发多次动作

问题分析与修复

你的CountDownTimer出现重复执行action的问题,核心原因是用根Job的cancelChildren()管理子协程的方式不够精准:

虽然调用job.cancelChildren()会尝试取消所有子协程,但协程取消是协作式的,如果在取消操作生效前,旧协程已经完成了delay并通过isActive检查,就会执行action()。连续调用start()时,这种时间差可能导致多个旧协程未被及时终止,最终多次触发动作。

修复方案

改为单独维护当前运行的定时器协程Job,每次启动前直接取消该Job,确保旧协程被彻底终止:

class CountDown(val action: () -> Unit, private val delayMillis: Long = 5000) {
    // 按需选择合适的调度器,这里保留原代码的Dispatchers.Default
    private val scope = CoroutineScope(Dispatchers.Default)
    private var currentTimerJob: Job? = null

    fun start() {
        // 取消当前正在运行的定时器协程
        currentTimerJob?.cancel()
        // 启动新的定时器协程,并保存引用
        currentTimerJob = scope.launch {
            delay(delayMillis)
            action()
        }
    }
}

关键改动说明

  1. 移除了固定的根Job,直接使用CoroutineScope(默认会绑定一个与生命周期一致的Job)
  2. 新增currentTimerJob变量,专门跟踪当前运行的定时器协程
  3. 每次调用start()时,先取消currentTimerJob(如果存在),再启动新协程
  4. 无需额外检查isActive:协程被取消后,delay()会立即抛出CancellationException,协程终止,不会执行后续的action()

这种方式能确保任意时刻只有一个定时器协程在运行,彻底解决重复执行的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 12:52:37