如何制作有保证的实时秒表Android

How to make a guaranteed real-time stopwatch Android

我正在尝试使用前台服务在我的应用程序中实现计时功能,本质上是一个秒表。下面是我正在做的事情的总体思路,当应用程序在前台时效果很好,但在屏幕关闭时效果不佳。

这是因为handler.postDelayed()不能保证是实时的,在熄屏的情况下,还差得很远

class TimerService: Service(), CoroutineScope {
    private var currentTime: Int = 0

    private val handler = Handler(Looper.getMainLooper())
    private var runnable: Runnable = object : Runnable {
        override fun run() {
            if (timerState == TimerState.START) {
                currentTime++
            }
            broadcastUpdate()
            // Repeat every 1 second
            handler.postDelayed(this, 1000)
        }
    }
    private val job = Job()
    override val coroutineContext: CoroutineContext
        get() = Dispatchers.IO + job

    // Call this to start the timer
    private fun startCoroutineTimer() {
        launch(coroutineContext) {
            handler.post(runnable)
        }
    }

    // Rest of the service class
    ...
}

我的问题是,做这样的事情的规范方法是什么?我需要能够使用前台服务来保证我可以制作一个能够启动、暂停、恢复和停止的准确秒表。

我的状态广播和其他一切都已经解决了,但实际的计时器部分是我卡住的地方,我似乎无法找到一个简单有效的解决方案来保证准确。

首先从不同的 OS 开始。有一整个 class 的 OSes 称为 RTOS(实时 OS)。 Linux(因此 Android)不是一个。如果您确实需要实时,linux 不是一个可接受的解决方案。

但假设您实际上不需要实时,您只需要更高的准确性。有几种方法可以轻松改进您的代码。

最重要的是,您当前的代码假定计时器每秒响一次。不要那样做。相反,请跟踪计时器启动的时间。每次计时器关闭时,获取当前时间。经过的时间是增量。这样,任何累积的不准确性都会在每个滴答声中消失。这也将解决很多屏幕关闭情况,因为屏幕打开后的第一次更新将更新到正确的时间。

private var timeStarted : Long = 0
private var timeElapsed : Long = 0
private var runnable: Runnable = object : Runnable {
    override fun run() {
        if (timerState == TimerState.START) {
            timeElapsed = System.getCurrentTimeMillis() - timeStarted
        }
        broadcastUpdate()
        // Repeat every 1 second
        handler.postDelayed(this, 1000)
    }
}

private fun startCoroutineTimer() {
    timeStarted = System.getCurrentTimeMillis()
    handler.post(runnable)
}

另请注意,您不需要协程来 post 处理程序。处理程序负责多线程,在那里启动协程没有任何价值。