如何制作有保证的实时秒表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 处理程序。处理程序负责多线程,在那里启动协程没有任何价值。
我正在尝试使用前台服务在我的应用程序中实现计时功能,本质上是一个秒表。下面是我正在做的事情的总体思路,当应用程序在前台时效果很好,但在屏幕关闭时效果不佳。
这是因为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 处理程序。处理程序负责多线程,在那里启动协程没有任何价值。