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

Android如何按顺序执行TimerTasks?解决并行执行问题

问题:如何让多个定时器任务按顺序执行?

我需要让多个定时器按顺序执行,前一个完成后再启动下一个。尝试用Handler类实现,结果却是定时器并行运行,输出如下。想问能不能让Timer操作阻塞线程直至完成?或者有更优的实现方式?比如Futures或Kotlin协程?我是Android开发新手,之前在iOS里用OperationQueue/Operation(设置isAsynchronous = true)实现过这个功能。

原实现代码

class SequentialTimerTasks {
    private val handlerThread: HandlerThread = HandlerThread("HandlerThread")
    private lateinit var threadHandler: Handler

    class TimerCountTask(private val id: Int) : TimerTask() {
        private val TAG = "TimerCountTask"
        var count = 0
        override fun run() {
            Log.d(TAG, "Runnable $id RUNNING TIMER $count")
            count++

            if (count >=10) {
                Log.d(TAG, "Runnable $id CANCEL TIMER $count")
                this.cancel()
            }
        }
    }

    class RunnableTask(private val id: Int) : Runnable {
        private val TAG = "RunnableTask"
        override fun run() {
            Log.d(TAG, "Runnable $id run() called")
            val timer = Timer()
            timer.schedule(TimerCountTask(id), 0, 1000)
        }
    }

    fun start() {
        handlerThread.start()
        threadHandler = Handler(handlerThread.looper)
        threadHandler.post(RunnableTask(1))
        threadHandler.post(RunnableTask(2))

    }
}

原代码输出

Runnable 1 run() called
Runnable 2 run() called
Runnable 1 RUNNING TIMER 0
Runnable 2 RUNNING TIMER 0
Runnable 2 RUNNING TIMER 1
Runnable 1 RUNNING TIMER 1
Runnable 2 RUNNING TIMER 2
Runnable 1 RUNNING TIMER 2
Runnable 2 RUNNING TIMER 3
Runnable 1 RUNNING TIMER 3
Runnable 2 RUNNING TIMER 4
Runnable 1 RUNNING TIMER 4
Runnable 2 RUNNING TIMER 5
Runnable 1 RUNNING TIMER 5
Runnable 2 RUNNING TIMER 6
Runnable 1 RUNNING TIMER 6
Runnable 2 RUNNING TIMER 7
Runnable 1 RUNNING TIMER 7

问题原因

你原代码里的每个RunnableTask启动了独立的Timer,而Timer本身会创建新线程执行定时任务。Handler只是把两个RunnableTask按顺序放到后台线程执行,但每个RunnableTask启动Timer后就立刻结束了,导致两个Timer同时并行运行。

解决方案

方案1:Kotlin协程(最推荐,Android主流方案)

协程能简洁实现顺序执行,用delay替代定时器,非阻塞且适配Android生命周期:

class SequentialTimerTasks {
    // 用Default调度器,适合后台计算任务
    private val scope = CoroutineScope(Dispatchers.Default + Job())

    suspend fun runTimerTask(id: Int) {
        val TAG = "CoroutineTimerTask"
        var count = 0
        while (count < 10) {
            Log.d(TAG, "Runnable $id RUNNING TIMER $count")
            // 非阻塞等待1秒,不会卡死线程
            delay(1000)
            count++
        }
        Log.d(TAG, "Runnable $id FINISHED TIMER")
    }

    fun start() {
        scope.launch {
            // 顺序执行:第一个任务完成后才会启动第二个
            runTimerTask(1)
            runTimerTask(2)
        }
    }

    // 记得在页面销毁时取消协程,避免内存泄漏
    fun cancel() {
        scope.cancel()
    }
}

这个方案和iOS的OperationQueue逻辑类似:CoroutineScope相当于队列,挂起函数suspend fun相当于异步Operation,天然支持顺序执行。

方案2:Handler+回调实现顺序执行

如果不想用协程,可以给定时任务添加完成回调,手动触发下一个任务:

class SequentialTimerTasks {
    private val handlerThread: HandlerThread = HandlerThread("HandlerThread")
    private lateinit var threadHandler: Handler

    class TimerCountTask(
        private val id: Int,
        private val onFinish: () -> Unit
    ) : TimerTask() {
        private val TAG = "TimerCountTask"
        var count = 0
        override fun run() {
            Log.d(TAG, "Runnable $id RUNNING TIMER $count")
            count++

            if (count >= 10) {
                Log.d(TAG, "Runnable $id CANCEL TIMER $count")
                this.cancel()
                // 任务完成后,通过Handler切回线程循环触发回调
                threadHandler.post(onFinish)
            }
        }
    }

    fun start() {
        handlerThread.start()
        threadHandler = Handler(handlerThread.looper)
        
        // 启动第一个任务,完成后再启动第二个
        Timer().schedule(TimerCountTask(1) {
            Timer().schedule(TimerCountTask(2) {
                // 更多任务可在此继续添加
            }, 0, 1000)
        }, 0, 1000)
    }
}

这种方式适合不依赖协程的场景,但任务较多时会出现回调嵌套,代码可读性下降。

方案3:ExecutorService+Future(Java风格)

用单线程线程池保证任务顺序,通过Thread.sleep模拟定时:

class SequentialTimerTasks {
    // 单线程池,所有任务按顺序执行
    private val executor = Executors.newSingleThreadExecutor()

    fun runTimerTask(id: Int) {
        val TAG = "FutureTimerTask"
        var count = 0
        while (count < 10) {
            Log.d(TAG, "Runnable $id RUNNING TIMER $count")
            // 阻塞等待1秒(注意:会占用线程,非推荐方式)
            Thread.sleep(1000)
            count++
        }
        Log.d(TAG, "Runnable $id FINISHED TIMER")
    }

    fun start() {
        executor.execute {
            // 单线程池内任务自动顺序执行
            runTimerTask(1)
            runTimerTask(2)
        }
    }

    // 记得在合适时机关闭线程池
    fun shutdown() {
        executor.shutdown()
    }
}

这种方式是阻塞式的,线程会被Thread.sleep占用,不如协程灵活,仅作为兼容Java代码的备选方案。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 12:40:24