Rewrite
As you said, you also noticed that the next time in onTick()
is calculated from the time the previous onTick()
ran, which introduces a tiny error on every tick. I changed the CountDownTimer source code to call each onTick()
at the specified intervals from the start time.
I build this upon the CountDownTimer framework, so cut & paste the source code into your project and give the class a unique name. (I called mine MoreAccurateTimer.) Now make a few changes:
-
Add a new class variable:
private long mNextTime;
-
Change
start()
:public synchronized final MoreAccurateTimer start() { if (mMillisInFuture <= 0) { onFinish(); return this; } mNextTime = SystemClock.uptimeMillis(); mStopTimeInFuture = mNextTime + mMillisInFuture; mNextTime += mCountdownInterval; mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG), mNextTime); return this; }
-
Change the Handler’s
handlerMessage()
:@Override public void handleMessage(Message msg) { synchronized (MoreAccurateTimer.this) { final long millisLeft = mStopTimeInFuture - SystemClock.uptimeMillis(); if (millisLeft <= 0) { onFinish(); } else { onTick(millisLeft); // Calculate next tick by adding the countdown interval from the original start time // If user's onTick() took too long, skip the intervals that were already missed long currentTime = SystemClock.uptimeMillis(); do { mNextTime += mCountdownInterval; } while (currentTime > mNextTime); // Make sure this interval doesn't exceed the stop time if(mNextTime < mStopTimeInFuture) sendMessageAtTime(obtainMessage(MSG), mNextTime); else sendMessageAtTime(obtainMessage(MSG), mStopTimeInFuture); } } }