倒计时器 CountDownTimer
一个非常简单易用的倒计时类,其实仍然是使用Handler来进行计时,不过封装好之后用起来方便多了。
一、10秒倒计时示例
new CountDownTimer(10000, 1000) {
@Override
public void onTick(long l) {
tvCountDown.setText(String.valueOf(l / 1000));
Log.e(TAG, "CountDownTimer long l = " + l);
}
@Override
public void onFinish() {
Log.e(TAG, "CountDownTimer onFinish");
}
}.start();
其中第一个参数为倒计时的总时间,第二个参数为Tick间隔时间,单位均是毫秒。
二、 onTick回调方法
onTick(long l)
回调方法返回的时间通常不是整数时间,比如上面的例子传入了 10000
和1000
,返回的参数如下:
MainActivity: CountDownTimer long l = 9999
MainActivity: CountDownTimer long l = 8999
MainActivity: CountDownTimer long l = 7997
MainActivity: CountDownTimer long l = 6995
MainActivity: CountDownTimer long l = 5995
MainActivity: CountDownTimer long l = 4993
MainActivity: CountDownTimer long l = 3992
MainActivity: CountDownTimer long l = 2990
MainActivity: CountDownTimer long l = 1988
MainActivity: CountDownTimer onFinish
没有一个是1000的整数倍的,总是少几毫米至十几毫秒,甚至只返回了9个参数。返回1988
之后,直接就onFinish()
了。啊?为什么会这样?!于是去找了一下源码,核心代码如下:
// handles counting down
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
synchronized (CountDownTimer.this) {
final long millisLeft = mStopTimeInFuture - SystemClock.elapsedRealtime();
if (millisLeft <= 0) {
onFinish();
} else if (millisLeft < mCountdownInterval) {
// no tick, just delay until done
sendMessageDelayed(obtainMessage(MSG), millisLeft);
} else {
long lastTickStart = SystemClock.elapsedRealtime();
onTick(millisLeft);
// take into account user's onTick taking time to execute
long delay = lastTickStart + mCountdownInterval - SystemClock.elapsedRealtime();
// special case: user's onTick took more than interval to
// complete, skip to next interval
while (delay < 0) delay += mCountdownInterval;
sendMessageDelayed(obtainMessage(MSG), delay);
}
}
}
};
其中 当millisLeft < mCountdownInterval
时,就不再调用onTick
方法了。然而,奇怪的是,我又找到了另一份源码,核心代码如下:
// 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);
}
}
}
};
按照上面这第二份源码,只要剩余时间大于零,应该就会回调onTick
方法。于是我把这份源码拷贝到工程,打印出这份代码的onTick
回调参数,如下:
MainActivity: CountDownTimer long l = 9999
MainActivity: CountDownTimer long l = 8998
MainActivity: CountDownTimer long l = 7996
MainActivity: CountDownTimer long l = 6993
MainActivity: CountDownTimer long l = 5991
MainActivity: CountDownTimer long l = 4989
MainActivity: CountDownTimer long l = 3987
MainActivity: CountDownTimer long l = 2985
MainActivity: CountDownTimer long l = 1983
MainActivity: CountDownTimer long l = 981
MainActivity: CountDownTimer onFinish
果然,onTick
方法回调了十次,这样也比小于间隔时间就直接回调onFinish
方法合理多了。
我查看了一下安卓的各版本源码,似乎api25
及之前,都是用第一份源码 CountDownTimer源码1,api26
及之后,就改为了第二份源码CountDownTimer源码2
最后
用完之后,别忘了调用 cancle()
方法。