当前位置: 首页 > 科技观察

把倒计时做到极致

时间:2023-03-21 02:06:14 科技观察

1.前言倒计时,需要每秒更新UI,应该是比较常见的。最常见的场景是发送验证码超时重试的逻辑。在这个逻辑中,需要一个倒计时逻辑,每秒修改UI,让倒计时能够被用户感知到。那么如何实现倒计时的逻辑呢?一个倒计时至少要有两个要求:准确和稳定。准确意味着应该执行两分钟的2分钟倒计时。稳定是指每次UI更新,间隔1s左右。二、实现思路1、通知UI更新倒计时,每次延迟1s。说白了,就是在固定的时间间隔做固定的任务。这种功能最简单的方法是使用Handler.postDelayed()间隔执行。那么我们来写一个C??ountdownUtils类,先看看它的结构。可以看到,它是基于一个Handler来延迟的。这个逻辑很简单,直接上代码。使用起来也很简单,2分钟就可以传入newCountdownUtils(120).start()看Log输出的结果。从Log来看确实是完成了一个倒计时功能,一秒一秒到0,但是这里为了观察是否准确,倒计时完成的时间已经记录为一个间隔。你看到任何问题吗?执行约124秒的120秒倒计时。这个问题其实是因为Handler.postDelayed()的间隔时间不是interval指定的准确时间。什么时候执行实际上取决于线程调度。这种总时间差的问题,不是换个Timer什么的就能解决的。对于这个问题,在一些验证码倒计时场景中,没有参考事件点,每次倒计时都有几十毫秒的误差,用户基本感知不到。但是在某些情况下,比如视频播放的倒计时,这个案例有参考,几分钟的倒计时,有几秒的误差,是非常明显的bug。这是不稳定的,那么如何让倒计时稳定呢?2.使用CountDownTimer实现倒计时。Android其实也提供了一个对应的支持类,就是CountDownTimer,它在android.os包下,可以完全实现一个倒计时逻辑。让我们先看看它是如何使用的。CountDownTimer的使用很简单,在onTick()中监听倒计时变化,结束时调用onFinish()。继续运行可以看到Log的输出。对于这个总时间,误差已经以毫秒为单位,这似乎比我们自己的实现要好得多。仔细一看,onTick()方法回调的参数是一个以毫秒为单位的值,这个值其实是不准确的,但这其实并没有影响。您只需要将其四舍五入即可获得正确的倒计时秒数。比如:2830就是3s,1828就是2s。但是仔细一看,就能发现问题所在。如果你用这种方式处理倒计时,你会发现如果拿不到1s的状态,直接3s-2s-finish。这个问题也可以从Log中反映出来。这很尴尬。有没有引用是个bug。只能先看看CountDownTimer的源码,它是如何保证总时长的准确性的。从CountDownTimer的结构体可以看出,其实是使用了mHandler的延时。继续看最重要的Handler的实现代码。在handleMessage()中,使用了一个SystemClock.elapsedRealtime(),实际上是获取了一个从系统启动到现在的绝对时间,包括系统休眠的时间间隔。然而,这并不重要。关键是CountDownTimer每次都会用这个时间计算一个相对于1s间隔的差值,也就是每次都对误差值进行修正,保证最终的总时长误差为毫秒级(其实就是最后一个postDelayed())的错误。既然找到了CountDownTimer保证时间准确的关键点,我们就可以重写第一个Demo的代码,解决no1s状态的问题。3.动态计算延迟值没什么好说的。就是计算耗时间隔,然后在接下来的1s中减去超过1s的毫秒值,修正间隔。现在已经实现了,我们来看看输出的Log。可以看出每次都是动态调整间隔,每秒更新一次状态,保证总时长在毫秒以内,基本解决了倒计时的问题。3.小结一个倒计时,单纯的使用Handler.postDelayed()是无法保证准确性和稳定性的。细节决定成败,倒计时也可以完美。【本文为专栏作家“张扬”原创稿件,转载请微信♂联系作者获得授权】点此查看作者更多好文