Introduction
In the development process, we often encounter some scenarios related to countdown, such as the button to send the verification code, which will display the countdown time after clicking the send button. After the countdown, we can refresh the button and allow clicking again. In order not to block the operation of the software, but also to refresh the interface in real time, we usually use Handler or AsyncTask technology to write our own logic implementation. In fact, Android has already packaged a CountDown Timer to fulfill this functional requirement.
CountDownTimer(long millisInFuture, long countDownInterval)
The two parameters of CountDownTime represent the total countdown time millisInFuture and the interval countDownInterval, respectively.
Specific calls are as follows:
TextView vertifyBtn; CountDownTimer timer = new CountDownTimer(60000, 1000) { @Override public void onTick(long millisUntilFinished) { vertifyBtn.setText((millisUntilFinished / 1000) + " second"); } @Override public void onFinish() { vertifyBtn.setEnabled(true); vertifyBtn.setText("Send"); } }; timer.start();
The above call example shows that the onTick method is executed once every second for a total of 60 seconds. Its parameter millisUntilFinished represents the milliseconds remaining in the countdown, and the onFinish method is executed at the end of the countdown.
Realization principle
Here's the source code for CountDownTimer, which is very understandable with very little code. As can be seen from the source code, CountDownTimer also uses Handler's message processing mechanism to achieve the effect. After initializing the start and termination time, it sends messages to the main thread through Handler at regular intervals, and then calls back the method in message processing. Make good use of the official packaged tools, we can avoid repeated wheel building, of course, better understand the principle of the wheel!
package android.os; public abstract class CountDownTimer { private final long mMillisInFuture; private final long mCountdownInterval; private long mStopTimeInFuture; private boolean mCancelled = false; public CountDownTimer(long millisInFuture, long countDownInterval) { mMillisInFuture = millisInFuture; mCountdownInterval = countDownInterval; } public synchronized final void cancel() { mCancelled = true; mHandler.removeMessages(MSG); } public synchronized final CountDownTimer start() { mCancelled = false; if (mMillisInFuture <= 0) { onFinish(); return this; } mStopTimeInFuture = SystemClock.elapsedRealtime() + mMillisInFuture; mHandler.sendMessage(mHandler.obtainMessage(MSG)); return this; } public abstract void onTick(long millisUntilFinished); public abstract void onFinish(); private static final int MSG = 1; 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 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); } } } }; }