注册

Android 实现计时器

这周接到个新需求,统计用户在线时长,累积到一定时长后上报,可以通过计时器来实现。本篇文章介绍下安卓端实现计时器的三种方式。


Timer、TimerTask


通过TimerTimerTask实现计时,代码如下:


class TimeChangeExample : BaseGestureDetectorActivity() {

private lateinit var binding: LayoutTimeChangeExampleActivityBinding

private val dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS")

private var timerHandler = object : Handler(Looper.myLooper() ?: Looper.getMainLooper()) {
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
if (msg.what == 0) {
setCountdownTimeText(msg.obj as Long)
}
}
}
private var timer: Timer? = null
private var timerTask: TimerTask? = null

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.layout_time_change_example_activity)
binding.btnCountdownByTimer.setOnClickListener {
clearText()
binding.tvCountdownText.text = "countdown by timer\n"
startCountdownByTime()
}
binding.btnStopTimer.setOnClickListener {
stopTimer()
}
}

private fun startCountdownByTime() {
stopTimer()
timerTask = object : TimerTask() {
override fun run() {
timerHandler.sendMessage(timerHandler.obtainMessage(0, System.currentTimeMillis()))
}
}
timer = Timer()
timer?.schedule(timerTask, 0, 1000)
}

private fun stopTimer() {
timer?.cancel()
timer = null
timerTask = null
}

private fun setCountdownTimeText(time: Long) {
binding.tvCountdownText.run {
post {
text = text.toString() + "${dateFormat.format(Date(time))}\n"
}
}
}

private fun clearText() {
binding.tvCountdownText.text = ""
}

override fun onDestroy() {
super.onDestroy()
stopTimer()
}
}

效果如图:


两次计时之间的误差都是毫秒级的。


timer -original-original.gif

BroadCastReceiver


通过注册广播,监听系统时间变化实现计时,但是广播回调触发的间隔固定为一分钟,代码如下:


class TimeChangeExample : BaseGestureDetectorActivity() {

private lateinit var binding: LayoutTimeChangeExampleActivityBinding

private val dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS")

private val timeChangeBroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
if (intent?.action == Intent.ACTION_TIME_TICK) {
setCountdownTimeText(System.currentTimeMillis())
}
}
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.layout_time_change_example_activity)
binding.btnCountdownByBroadcast.setOnClickListener {
clearText()
binding.tvCountdownText.text = "countdown by broadcast\n"
startCountdownByBroadcast()
}
binding.btnStopBroadcast.setOnClickListener {
stopBroadcast()
}
}

private fun startCountdownByBroadcast() {
registerReceiver(timeChangeBroadcastReceiver, IntentFilter().apply {
addAction(Intent.ACTION_TIME_TICK)
})
}

private fun stopBroadcast() {
unregisterReceiver(timeChangeBroadcastReceiver)
}

private fun setCountdownTimeText(time: Long) {
binding.tvCountdownText.run {
post {
text = text.toString() + "${dateFormat.format(Date(time))}\n"
}
}
}

private fun clearText() {
binding.tvCountdownText.text = ""
}

override fun onDestroy() {
super.onDestroy()
stopBroadcast()
}
}

效果如图:


两次计时之间的误差都是毫秒级的。


boardcast.png

Handler


通过HandlerRunnable来实现计时,代码如下:


class TimeChangeExample : BaseGestureDetectorActivity() {

private lateinit var binding: LayoutTimeChangeExampleActivityBinding

private val dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS")

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.layout_time_change_example_activity)
binding.btnCountdownByHandler.setOnClickListener {
clearText()
binding.tvCountdownText.text = "countdown by handler\n"
startCountdownByHandler()
}
binding.btnStopHandler.setOnClickListener {
stopHandler()
}
}

private val handler = Handler(Looper.myLooper() ?: Looper.getMainLooper())
private val countdownRunnable = object : Runnable {
override fun run() {
setCountdownTimeText(System.currentTimeMillis())
val currentTime = SystemClock.uptimeMillis()
val nextTime = currentTime + (1000 - currentTime % 1000)
handler.postAtTime(this, nextTime)
}
}

private fun startCountdownByHandler() {
val currentTime = SystemClock.uptimeMillis()
val nextTime = currentTime + (1000 - currentTime % 1000)
handler.postAtTime(countdownRunnable, nextTime)
}

private fun stopHandler() {
handler.removeCallbacks(countdownRunnable)
}

private fun setCountdownTimeText(time: Long) {
binding.tvCountdownText.run {
post {
text = text.toString() + "${dateFormat.format(Date(time))}\n"
}
}
}

private fun clearText() {
binding.tvCountdownText.text = ""
}

override fun onDestroy() {
super.onDestroy()
stopHandler()
}
}

效果如图:


两次计时之间的误差都是毫秒级的。


handler -original-original.gif

示例


在示例Demo中添加了相关的演示代码。


ExampleDemo github


ExampleDemo gitee


作者:ChenYhong
链接:https://juejin.cn/post/7214288126223319100
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

0 个评论

要回复文章请先登录注册