【问题标题】:Android - Run TimerTasks in SequenceAndroid - 按顺序运行 TimerTasks
【发布时间】:2022-11-29 14:22:04
【问题描述】:

我想按顺序运行多个计时器。当一个计时器完成时,下一个应该开始。我想过使用 Handler 类,但这具有并行运行计时器的效果。从下面的输出可以看出。

有没有办法让 Timer 操作阻塞线程直到它完成或者有更好的方法来实现这个?也许使用 Futures 或 Kotlin Coroutines?

我是安卓新手。在 iOS 上,我可以使用 OperationQueue/Operation(set 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

【问题讨论】:

  • 我认为您需要创建“队列事件”在工作线程上执行指定的 Runnable。
  • 您只想要基于处理程序的解决方案,还是可以使用 Kotlin 协程?
  • @ArpitShukla 协程将是一个不错的选择。我一直在阅读它们,但不确定如何按顺序实现多个计时器/重复任务
  • 你在你的代码中使用 android ViewModels 吗?
  • 不,我只是将它作为一个没有生命周期或其他平台依赖性的简单对象运行

标签: android multithreading kotlin timer kotlin-coroutines


【解决方案1】:

你对 HandlerThread 的使用是多余的——这个类在你的代码中没有做任何有用的事情,而且它的线程并不比你自己启动的任何随机线程或其他库创建的线程好。当然,它有一个 Looper/Handler,但是您没有对 Handler 做任何有用的事情来证明它的存在。

Timer 是 Java 5 之前的 Java 版本的古老遗产。它的存在早于 Android,现在没有人应该使用它。引用 Timer 的文档:

Java 5.0 引入了 java.util.concurrent 包,其中一个并发实用程序是 ScheduledThreadPoolExecutor,它是一个线程池,用于以给定的速率或延迟重复执行任务。它实际上是 Timer 的更通用的替代品

您的代码中最大的错误是使用了 2 个独立的线程池:一个单线程的HandlerThread“池”和一个由Timer 内部使用的隐藏线程池。通常,您应该始终尽量减少代码中多线程的使用,并将“正常”代码与多线程工件隔离开来。使用多种类型的线程(Android 主线程除外)与该目的背道而驰。

理论上可以修复您的原始代码以按预期工作,但由于上述原因,我建议放弃 HandlerThreadTimer 并切换到基于 ScheduledThreadPoolExecutor 的单线程池:

final int PERIOD = 1000;
final int SECOND_TASK_DEADLINE = 10000;

final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();

final Runnable firstTask = new Runnable() {
  int count = 0;

  public void run() {
    count++;
    Log.d(TAG, "Runnable 1 RUNNING, timer " + count);
  }
};

final Runnable secondTask = new Runnable() {
  int count = 0;

  public void run() {
    count++;
    Log.d(TAG, "Runnable 2 RUNNING, timer " + count);
  }
};

final Future<?> firstFuture = executorService.scheduleAtFixedRate(firstTask, 0, PERIOD, TimeUnit.MILLISECONDS);

executorService.schedule(new Runnable() {
  public void run() {
    firstFuture.cancel(false);

    executorService.scheduleAtFixedRate(secondTask, 0, PERIOD, TimeUnit.MILLISECONDS);
  }
}, SECOND_TASK_DEADLINE, TimeUnit.MILLISECONDS);

与您的原始代码不同,上面的代码仅在 1 个线程上运行您的繁忙逻辑 — ScheduledThreadPoolExecutor 的线程。

这使您可以利用自然互斥:尽管没有执行显式线程同步,但您的代码永远不会与自身竞争或并发运行……因为只有 1 个线程。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-08
    • 1970-01-01
    相关资源
    最近更新 更多