countdowntimer
CountDownTimer
在做项目的时候,如果涉及到倒计时的功能的时候,都会使用到这个类,但是,常在河边走,哪有不湿身的。虽然经常使用,但是还是掉到了一次坑里面。
掉坑经历
背景
智能家居有这样一个需求,在设备成功配网以后,第一次初始化设备的shadow时比较耗时,所以要求15秒内,每3秒请求一次,有一次成功,则完成设备的初始化。
解决方案
15秒内,所以使用到了countDownTimer做倒计时;
每3秒请求一次,则使用Timer和TimerTask;
timerTask每三秒执行一次请求shadow状态的topic,有一次成功获取到shadow的正确状态,则关闭countDownTimer、timer、timerTask;
掉坑
关闭countDownTimer,调用了cancel();
关闭timer,调用了cancel();
关闭timerTask,调用了cancel();
坑就在这系列cancel(),countDownTimer的onFinish()回调里关闭timer和timerTask。
弃坑
背景
15秒内成功请求到了shadow的正确状态,但是timerTask还在一直执行,什么鬼,不是全部cancel()了吗?
解决方案
然后就一路debug,突然发现,执行countDownTimer的cancel()方法后,并没有调用onFinish(),纳尼,这是什么情况?
进入CountDownTimer源码:
public abstract class CountDownTimer {
/**
* Millis since epoch when alarm should stop.
* 倒计时的总时长
*/
private final long mMillisInFuture;
/**
* The Interval in millis that the user receives callbacks
* 收到回调的间隔时间
*/
private final long mCountdownInterval;
private long mStopTimeInFuture;
/**
* boolean representing if the timer was cancelled
*/
private boolean mCancelled = false;
/**
* @param millisInFuture The number of millis in the future from the call
* to {@link #start()} until the countdown is done and {@link #onFinish()}
* is called.
* @param countDownInterval The interval along the way to receive
* {@link #onTick(long)} callbacks.
*/
public CountDownTimer(long millisInFuture, long countDownInterval) {
mMillisInFuture = millisInFuture;
mCountdownInterval = countDownInterval;
}
/**
* Cancel the countdown.
*/
public synchronized final void cancel() {
mCancelled = true;
mhandler.removemessages(MSG);
}
/**
* Start the countdown.
*/
public synchronized final CountDownTimer start() {
mCancelled = false;
//总时长小于等于0,则调用onFinish()
if (mMillisInFuture <= 0) {
onFinish();
return this;
}
mStopTimeInFuture = SystemClock.elapsedRealtime() + mMillisInFuture;
mHandler.sendMessage(mHandler.obtainMessage(MSG));
return this;
}
/**
* Callback fired on regular interval.
* @param millisUntilFinished The amount of time until finished.
*/
public abstract void onTick(long millisUntilFinished);
/**
* Callback fired when the time is up.
*/
public abstract void onFinish();
private static final int MSG = 1;
// handles counting down
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
synchronized (CountDownTimer.this) {
if (mCancelled) {
return;
}
final long millisLeft = mStopTimeInFuture - SystemClock.elapsedRealtime();
if (millisLeft <= 0) {
onFinish();
} else {
long lastTickStart = SystemClock.elapsedRealtime();
onTick(millisLeft);
// take into account user's onTick taking time to execute
long lastTickDuration = SystemClock.elapsedRealtime() - lastTickStart;
long delay;
if (millisLeft < mCountdownInterval) {
// just delay until done
delay = millisLeft - lastTickDuration;
// special case: user's onTick took more than interval to
// complete, trigger onFinish without delay
if (delay < 0) delay = 0;
} else {
delay = mCountdownInterval - lastTickDuration;
// special case: user's onTick took more than interval to
// complete, skip to next interval
while (delay < 0) delay += mCountdownInterval;
}
sendMessageDelayed(obtainMessage(MSG), delay);
}
}
}
};
}
cancel()方法
/**
* Cancel the countdown.
*/
public synchronized final void cancel() {
mCancelled = true;
mHandler.removeMessages(MSG);
}
发现并没有调用onFinish()方法,只是发送了移除message的信息。所以,调用cancel()方法并不会调用onFinish()方法,只是调用handler的removeMessages()方法。
相关阅读
php 内置变量DIRECTORY_SEPARATOR的使用
widows下的路径分隔符为 / or \ ,linux下的路径分隔符为 /DIRECTORY_SEPARATOR是一个返回跟操作系统相关的路径分隔符的php内置
Excel在当今社会运用得比较广泛,它通常作用于数据的统计、分析、对比等。而最近也很流行用IPAD查看excel,因为这样很方便,所以今天小
一、layer的icon样式 以上样式测试代码: layer.confirm('icon测试', {icon: 1, title:'提示'}, function(index){
Vue中使用 transition标签或transition-group标签以及
Vue的动画并没有非常炫酷的效果,不过也是有一些实用性的,在项目中有的地方使用,也是能够营造出不同的效果 下面为大家列举两个简单实
文章目录导读乐观锁CAS 原理ABA问题库表改造代码改造RedPacketDao新增接口方法及Mapper映射文件UserRedPacketServic接口及实现类