开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 16 天,点击查看活动详情
倒计时功能相信大家都遇到过,特别是在获取验证码时,为了防止用户频繁获取会设置一个倒计时,通常是60s,超过时限后又可继续点击获取按钮。还有就是电商应用中某些商品活动、下单未支付的待支付倒计时等。
其实实现的方式相当多,例如Handler配合sendEmptyMessageDelayed()或者Runnable,Time配合TimeTask。又或者早期的RxJava系列的interval操作符,Kotlin中的Flow(这里涉及协程)等等。但如果不稍加处理,其中都会存在一个问题:当倒计时正在进行的时候退出该页面,之后再次进入页面以前的时间又重置了,不能达到真正的倒计时功能。那么,如何实现真正的倒计时功能呢?本文就使用原生工具CountDownTimer实现一个真正的倒计时工具(其他方式也可效仿实现)。
首先回顾一下CountDownTimer的使用方法:
public abstract class CountDownTimer {
public CountDownTimer(long millisInFuture, long countDownInterval) {
throw new RuntimeException("Stub!");
}
public final synchronized void cancel() {
throw new RuntimeException("Stub!");
}
public final synchronized CountDownTimer start() {
throw new RuntimeException("Stub!");
}
public abstract void onTick(long var1);
public abstract void onFinish();
}
构造方法内就只有倒计时总时长和间隙时长两个属性,其次onTick方法为剩余时长,其他的就是一些开始结束和取消方法。
上面说到我们需要实现真正的倒计时功能,那么要满足需求我们就要把当前的时间戳和倒计时时长做一个绑定。如此一来,不管我在哪个页面,我只需记录倒计时开始的时间,问题就迎刃而解。这里我们把目标时间保存在本地:
private val mCountDownTimer: CountDownTimer
get() {
return object : CountDownTimer(getCountTimeMills(),1000){
override fun onTick(millisUntilFinished: Long) {
mListener?.onTick(millisUntilFinished/1000)
}
override fun onFinish() {
mListener?.onFinish()
}
}
}
//开始倒计时,入参总时长 保存目标时间戳为结束时间
fun start(totalTimeMills: Long){
mTotalTimeMills = totalTimeMills+System.currentTimeMillis()
MMKV.defaultMMKV().encode(KEY_TIME, mTotalTimeMills)
mCountDownTimer.start()
}
//提供给CountDownTimer入参总时长
fun getCountTimeMills(): Long{
mTotalTimeMills = MMKV.defaultMMKV().decodeLong(KEY_TIME)
return mTotalTimeMills - System.currentTimeMillis()
}
另外,还需添加方法判断当前时间是否已经超过倒计时结束时间,仅当返回为true时才能继续调用mCountDownTimer.start():
fun isCounting(): Boolean{
mTotalTimeMills = MMKV.defaultMMKV().decodeLong(KEY_TIME)
return System.currentTimeMillis() < mTotalTimeMills
}
到此再添加一个接口,用于回调CountDownTimer的onTick和onFinish方法,给外部提供更新视图的入口。因为CountDownTimer内部也是使用的Handler,这里已经切换为UI线程,所以可以直接更新UI。另外在Activity中使用CountDownTimer时,需要在页面销毁的时候调用countDownTimer.cancel(),防止内存泄漏。
总结
精髓其实就是当前时间和倒计时时长做绑定,本地记录下倒计时完成的时间戳。这样就能在任何地方知道倒计时是否完成,并计算出剩余的倒计时显示到页面。特别需要注意的是要熟悉说选择倒计时的基础原理,才能在此基础上更好的掌握封装,例如上文所使用的CountDownTimer,底层也是使用的Handler,需要在离开页面时进行释放,否则很容易导致内存泄漏。
开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 16 天,点击查看活动详情