Android CountDownTimer 计时不准确问题
问题
使用CountDownTimer
计时并不精准, 比如定时5分钟, 结果4:50多几秒钟就停止计时, 解决方式参考了Google TextClock
控件
private final Runnable mTicker = new Runnable() {
public void run() {
if (mStopTicking) {
return; // Test disabled the clock ticks
}
onTimeChanged();
long now = SystemClock.uptimeMillis();
long next = now + (1000 - now % 1000);
Handler handler = getHandler();
if (handler != null) {
handler.postAtTime(mTicker, next);
}
}
};
解决方式
无需重写CountDownTimer
private val mTvDownTimer: TextView by lazy { findViewById(R.id.tv_countdown_timer) }
private val mCount: Long = 10
private val mCountDownTimer: CountDownTimer = object : CountDownTimer(mCount * 1000, 1000) {
override fun onTick(millisUntilFinished: Long) {
val next: Long = millisUntilFinished + (1000 - millisUntilFinished % 1000)
val showTime: Long = Math.min(mCount, next / 1000)
mTvDownTimer.text = "CountDownTimer 👉 $showTime"
Log.w("123", "onTick millisUntilFinished=$millisUntilFinished next=$next showTime=$showTime")
}
override fun onFinish() {
mTvDownTimer.text = "CountDownTimer 👉 0"
Log.e("123", "onFinish")
}
}
🍎 Log 日志 (注意第一行next=11000
是超过了mCount
的最大值,所以需要Math.min
函数取小)
W/123: onTick millisUntilFinished=10000 next=11000 showTime=10
W/123: onTick millisUntilFinished=8998 next=9000 showTime=9
W/123: onTick millisUntilFinished=7996 next=8000 showTime=8
W/123: onTick millisUntilFinished=6993 next=7000 showTime=7
W/123: onTick millisUntilFinished=5991 next=6000 showTime=6
W/123: onTick millisUntilFinished=4989 next=5000 showTime=5
W/123: onTick millisUntilFinished=3987 next=4000 showTime=4
W/123: onTick millisUntilFinished=2985 next=3000 showTime=3
W/123: onTick millisUntilFinished=1984 next=2000 showTime=2
W/123: onTick millisUntilFinished=983 next=1000 showTime=1
E/123: onFinish
🍌 再加上暂停/继续
功能 (GitHub 👉 https://github.com/javakam/AndoTemplate/blob/master/ando_toolkit/src/main/java/ando/toolkit/FixedCountDownTimer.java
)
完整代码:
/**
* 基于 API 30 的 CountDownTimer 做了以下调整:
* <p>
* 1. 修复计时不准的问题;
* 2. 增加 暂停/继续
* </p>
*
* @author javakam
*/
public class FixedCountDownTimer {
public interface Listener {
void onTick(long fixedMillisUntilFinished);
void onFinish();
}
private static final long SECOND_MILLIS = 1000L;
/**
* 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;
/**
* 剩余时间(单位毫秒)
*/
private long mMillisUntilFinished;
/**
* boolean representing if the timer was cancelled
*/
private boolean mCancelled = false;
/**
* 暂停的状态
*/
private boolean mPaused = false;
/**
* 是否正在倒计时
*/
private boolean mIsRunning = false;
private Listener mListener;
public FixedCountDownTimer(long millisInFuture, long countDownInterval) {
//注: 由于 sendMessageDelayed 的机制造成的 1000 毫秒的延时
mMillisInFuture = millisInFuture;
mCountdownInterval = countDownInterval;
}
public void setListener(Listener l) {
this.mListener = l;
}
/**
* Start the countdown.
*/
public synchronized final FixedCountDownTimer start() {
mCancelled = false;
if (mMillisInFuture <= 0) {
onFinish();
return this;
}
mStopTimeInFuture = SystemClock.elapsedRealtime() + mMillisInFuture;
mHandler.sendMessage(mHandler.obtainMessage(MSG));
mIsRunning = true;
mPaused = false;
return this;
}
public synchronized final void pause() {
mMillisUntilFinished = mStopTimeInFuture - SystemClock.elapsedRealtime();
mIsRunning = false;
mPaused = true;
}
public synchronized long resume() {
// 结束的时间设置为当前时间加剩余时间
mStopTimeInFuture = SystemClock.elapsedRealtime() + mMillisUntilFinished;
mHandler.sendMessage(mHandler.obtainMessage(MSG));
mIsRunning = true;
mPaused = false;
return mMillisUntilFinished;
}
/**
* Cancel the countdown.
*/
public synchronized final void cancel() {
mHandler.removeMessages(MSG);
mCancelled = true;
mIsRunning = false;
mPaused = false;
}
/**
* Callback fired on regular interval.
*
* @param millisUntilFinished The amount of time until finished.
*/
private void onTick(long millisUntilFinished) {
if (mListener != null) {
//修复时间不准的问题, 参考 TextClock
final long fixedMillisUntilFinished = millisUntilFinished + (SECOND_MILLIS - millisUntilFinished % SECOND_MILLIS);
/*
millisUntilFinished=1998; fixedMillisUntilFinished=2000
millisUntilFinished=997; fixedMillisUntilFinished=1000
...
*/
Log.w("123", "millisUntilFinished=" + millisUntilFinished + "; fixedMillisUntilFinished=" + fixedMillisUntilFinished);
mListener.onTick(fixedMillisUntilFinished);
}
}
private void onFinish() {
mIsRunning = false;
if (mListener != null) {
mListener.onFinish();
}
}
public boolean isPaused() {
return mPaused;
}
public boolean isRunning() {
return mIsRunning;
}
private static final int MSG = 1;
// handles counting down
private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
synchronized (FixedCountDownTimer.this) {
if (mCancelled) {
return;
}
if (mPaused) {
removeMessages(MSG);
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);
}
}
}
};
}
使用时候注意先判断是否处于运行中
if (mFixedCountDownTimer!=null&&!mFixedCountDownTimer.isRunning()) {
if (mFixedCountDownTimer.isPaused()) {
mFixedCountDownTimer.resume()
} else {
mFixedCountDownTimer.start()
}
}
参考:
本文作者:风之旅人
本文链接:https://www.cnblogs.com/jooy/p/17302010.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。