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()
    }
}
相关推荐
峥嵘life10 小时前
Android16 EDLA【GTS】GtsPermissionTestCases存在fail项
android·学习
魑魅魍魉都是鬼10 小时前
Android:java kotlin 单例模式
android·java·单例模式
俩个逗号。。10 小时前
Kotlin 扩展函数详解
开发语言·kotlin
段娇娇19 小时前
Android jetpack LiveData(一)使用篇
android·android jetpack
XiaoLeisj19 小时前
Android Jetpack 页面架构实战:从 LiveData、ViewModel 到 DataBinding 的生命周期管理与数据绑定
android·java·架构·android jetpack·livedata·viewmodel·databinding
似水明俊德1 天前
15-C#
android·开发语言·c#
阿拉斯攀登1 天前
第 19 篇 驱动性能优化与功耗优化实战
android·驱动开发·瑞芯微·嵌入式驱动·安卓驱动
91刘仁德1 天前
C++ 内存管理
android·c语言·数据结构·c++·经验分享·笔记·算法
小强开学前1 天前
自定义 Drawable 实现任意高度纯圆角背景及玻璃效果
android
秃了也弱了。1 天前
ElasticSearch:优化案例实战解析(持续更新)
android·java·elasticsearch