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
相关产品推荐
相关产品推荐

