Android kotlin 定时n秒完成时回调,含暂停和继续

Kotlin 复制代码
object TimerTaskUtil {
    // 协程任务对象,控制定时任务生命周期
    private var timerJob: Job? = null
    // 定时任务总时长(毫秒)- 初始化时赋值,全程不变
    private var totalDelayMs: Long = 0
    // 已执行的计时时长(毫秒)- 暂停时累加,恢复时基于此计算剩余时长
    private var elapsedMs: Long = 0
    // 计时开始时间戳(毫秒)- 用于计算单次运行的已耗时
    private var startTime: Long = 0
    // 计时状态流 - 对外暴露不可变状态,支持外部监听计时状态变化
    private val _timerState = MutableStateFlow(TimerState.IDLE)
    val timerState: StateFlow<TimerState> = _timerState.asStateFlow()

    /**
     * 计时状态枚举
     * IDLE:空闲(未启动/已完成/已取消)
     * RUNNING:运行中
     * PAUSED:已暂停
     */
    enum class TimerState {
        IDLE, RUNNING, PAUSED
    }

    /**
     * 启动定时任务(重置所有状态,重新开始计时)
     * @param delaySeconds 延迟执行的秒数(定时总时长)
     * @param onFinish 任务结束后的回调(主线程执行,可直接更新UI)
     */
    fun startTimer(delaySeconds: Long, onFinish: () -> Unit) {
        // 取消已有任务,重置所有计时状态
        cancelTimer()
        totalDelayMs = TimeUnit.SECONDS.toMillis(delaySeconds)
        elapsedMs = 0
        startTime = System.currentTimeMillis()
        _timerState.value = TimerState.RUNNING

        // 启动新的协程任务(IO作用域不阻塞主线程)
        timerJob = CoroutineScope(Dispatchers.IO).launch {
            // 延迟剩余未执行的时长
            delay(totalDelayMs - elapsedMs)
            // 计时完成,重置状态并执行主线程回调
            launch(Dispatchers.Main) {
                _timerState.value = TimerState.IDLE
                onFinish()
            }
        }
    }

    /**
     * 暂停计时任务(仅在运行中时生效)
     * 暂停后保留当前计时进度,可通过resumeTimer恢复
     */
    fun pauseTimer() {
        if (_timerState.value != TimerState.RUNNING) return

        // 取消当前协程任务,停止计时
        timerJob?.cancel()
        // 计算本次运行的耗时,累加到总已耗时
        val currentTime = System.currentTimeMillis()
        elapsedMs += currentTime - startTime
        // 更新计时状态为暂停
        _timerState.value = TimerState.PAUSED
    }

    /**
     * 恢复计时任务(仅在暂停时生效)
     * 基于暂停前的进度,继续完成剩余计时
     */
    fun resumeTimer() {
        if (_timerState.value != TimerState.PAUSED) return

        // 重置开始时间戳,启动新的协程任务
        startTime = System.currentTimeMillis()
        _timerState.value = TimerState.RUNNING
        timerJob = CoroutineScope(Dispatchers.IO).launch {
            // 延迟剩余未执行的时长(总时长 - 已耗时)
            delay(totalDelayMs - elapsedMs)
            // 计时完成,重置状态并执行主线程回调
            launch(Dispatchers.Main) {
                _timerState.value = TimerState.IDLE
            }
        }
    }

    /**
     * 取消当前定时任务(清空所有状态,回到空闲)
     * 页面销毁/不再需要计时时调用,避免内存泄漏
     */
    fun cancelTimer() {
        // 取消协程任务
        timerJob?.cancel()
        // 重置所有计时状态
        timerJob = null
        totalDelayMs = 0
        elapsedMs = 0
        startTime = 0
        _timerState.value = TimerState.IDLE
    }

    /**
     * 兼容原有方法:判断定时任务是否正在执行
     */
    fun isTimerRunning(): Boolean {
        return _timerState.value == TimerState.RUNNING
    }

    /**
     * 扩展方法:获取当前剩余计时时长(秒,向下取整)
     * 可用于UI展示剩余时间
     */
    fun getRemainingSeconds(): Long {
        if (_timerState.value == TimerState.IDLE) return 0
        // 计算剩余毫秒数,转换为秒
        val remainingMs = totalDelayMs - elapsedMs
        return if (_timerState.value == TimerState.RUNNING) {
            // 运行中:实时计算剩余时长(总剩余 - 本次已运行时长)
            val currentRunMs = System.currentTimeMillis() - startTime
            Math.max(0, (remainingMs - currentRunMs) / 1000)
        } else {
            // 暂停/空闲:直接返回固定剩余时长
            Math.max(0, remainingMs / 1000)
        }
    }
}

使用

Kotlin 复制代码
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.TextView
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch

class MainActivity : AppCompatActivity() {
    private lateinit var tvRemaining: TextView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        tvRemaining = findViewById(R.id.tv_remaining_time)

        // 启动10秒定时任务
        TimerTaskUtil.startTimer(10) {
            tvRemaining.text = "计时完成!"
        }

        // 每秒刷新一次剩余时间展示
        lifecycleScope.launch {
            while (isActive) { // 核心:依托lifecycleScope的isActive,页面销毁时协程自动取消
                // 仅当页面处于「可见可交互」状态(STARTED)时更新UI,后台时暂停刷新
                if (lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) {
                    val remaining = TimerTaskUtil.getRemainingSeconds()
                   LogUtil.d("剩余:$remaining 秒")
                }
                delay(1000) // 每秒刷新一次,非阻塞协程
            }
        }
    }

    // 页面销毁时取消定时任务,避免内存泄漏
    override fun onDestroy() {
        super.onDestroy()
        TimerTaskUtil.cancelTimer()
    }
}
相关推荐
2501_937189232 小时前
TV 电视影视大全:全场景高清观影技术解析
android·源码·源代码管理
2501_937154932 小时前
TV 电视影视大全:全场景高清观影技术解析
android·源码·源代码管理·机顶盒
峥嵘life2 小时前
Android16 【GSI】CtsMediaCodecTestCases等一些列Media测试存在Failed项
android·linux·运维·服务器·学习
玉梅小洋2 小时前
Android SDK 安装指南(MacOS 和 Windows)
android·windows·macos·sdk
2501_916007472 小时前
没有 Mac 用户如何上架 App Store,IPA生成、证书与描述文件管理、跨平台上传
android·macos·ios·小程序·uni-app·iphone·webview
stevenzqzq2 小时前
kotlin和compose中使用by
kotlin·compose
常利兵2 小时前
Spring Boot 3 多数据源整合 Druid:监控页面与控制台 SQL 日志配置实战
android·spring boot·sql
独自破碎E2 小时前
【BISHI11】变幻莫测
android·java·开发语言
CODECOLLECT2 小时前
工业安卓 Telnet 工具横向测评:5 大核心维度对比
android