Android 实现计时器
这周接到个新需求,统计用户在线时长,累积到一定时长后上报,可以通过计时器来实现。本篇文章介绍下安卓端实现计时器的三种方式。
Timer、TimerTask
通过Timer
和TimerTask
实现计时,代码如下:
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()
}
}
效果如图:
两次计时之间的误差都是毫秒级的。
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()
}
}
效果如图:
两次计时之间的误差都是毫秒级的。
Handler
通过Handler
和Runnable
来实现计时,代码如下:
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()
}
}
效果如图:
两次计时之间的误差都是毫秒级的。
示例
在示例Demo中添加了相关的演示代码。
作者:ChenYhong
链接:https://juejin.cn/post/7214288126223319100
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
链接:https://juejin.cn/post/7214288126223319100
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。