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()
    }
}
相关推荐
alexhilton11 小时前
使用FunctionGemma进行设备端函数调用
android·kotlin·android jetpack
冬奇Lab14 小时前
InputManagerService:输入事件分发与ANR机制
android·源码阅读
张小潇17 小时前
AOSP15 Input专题InputManager源码分析
android·操作系统
lhDream19 小时前
Kotlin 开发者必看!JetBrains 开源 LLM 框架 Koog 快速上手指南(含示例)
kotlin
RdoZam19 小时前
Android-封装基类Activity\Fragment,从0到1记录
android·kotlin
奥陌陌1 天前
android 打印函数调用堆栈
android
用户985120035831 天前
Compose Navigation 3 深度解析(二):基础用法
android·android jetpack
恋猫de小郭1 天前
Android 官方正式官宣 AI 支持 AppFunctions ,Android 官方 MCP 和系统级 OpenClaw 雏形
android·前端·flutter
黄林晴1 天前
Android 17 Beta 2,隐私这把锁又拧紧了
android
Kapaseker1 天前
研究表明,开发者对Kotlin集合的了解不到 20%
android·kotlin