Countdown function, for example: send SMS verification code countdown.
1 public class CountDownTimerActivity extends Activity { 2 3 private Button mSend; 4 private SendCountMessage mCountMessage; 5 6 @Override 7 protected void onCreate(Bundle savedInstanceState) { 8 super.onCreate(savedInstanceState); 9 this.setContentView(R.layout.activity_countdown); 10 11 mCountMessage = new SendCountMessage(); 12 mSend = (Button) findViewById(R.id.sendCode); 13 mSend.setOnClickListener(new View.OnClickListener() { 14 @Override 15 public void onClick(View v) { 16 mSend.setClickable(false); 17 //Start countdown function 18 mCountMessage.start(); 19 } 20 }); 21 } 22 23 24 /** 25 * We inherit the abstract class and set the total countdown time and interval time 26 * And override the onTick and onFinish methods 27 */ 28 class SendCountMessage extends CountDownTimer { 29 30 /** 31 * Here we also need to set two parameters: 32 * The first parameter: the total time we count down 33 * The second parameter: it indicates the interval of our countdown, such as whether we press one second or two seconds 34 */ 35 public SendCountMessage() { 36 super(60000, 1000); 37 } 38 39 /** 40 * This method means that this method will be called at the interval set in the constructor. 41 * For example, if we set the interval to 1 second, then CountDownTimer 42 * Call the onTick method every second 43 * @param millisUntilFinished Indicates the end time of distance countdown 44 */ 45 public void onTick(long millisUntilFinished) { 46 mSend.setText(millisUntilFinished/1000 + " Second second retransmission"); 47 } 48 49 /** 50 * This is the end of the countdown 51 */ 52 public void onFinish() { 53 mSend.setClickable(true); 54 } 55 } 56 57 @Override 58 protected void onDestroy() { 59 super.onDestroy(); 60 /** 61 * In the end, we need to cancel CountDownTimer, because if we are destroying the interface 62 * If the countdown timer has not been cancelled, it will continue to run in the background until the end of the countdown, 63 * In order to avoid problems, we need to cancel it and let the system gc the variable. 64 */ 65 if(mCountMessage != null) { 66 mCountMessage.cancel(); 67 mCountMessage = null; 68 } 69 } 70 }
Interface layout:
1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:orientation="vertical"> 6 7 ........ 8 9 <LinearLayout 10 android:layout_width="fill_parent" 11 android:layout_height="wrap_content" 12 android:layout_marginBottom="20dip" 13 android:layout_marginLeft="10dip" 14 android:layout_marginRight="10dip" 15 android:layout_marginTop="20dip"> 16 17 <EditText 18 android:layout_width="0dip" 19 android:layout_height="wrap_content" 20 android:layout_weight="1" 21 android:inputType="number" 22 android:hint="Please enter the verification code" /> 23 24 <Button 25 android:id="@+id/sendCode" 26 android:layout_width="wrap_content" 27 android:layout_height="wrap_content" 28 android:text="Send verification code" 29 android:textSize="18sp" /> 30 31 </LinearLayout> 32 33 </LinearLayout>
When we don't need to use the countdown function, we must call cancel() to cancel it, otherwise it will continue to execute when our page is destroyed, which is likely to cause memory leakage
code analysis
1 public CountDownTimer(long millisInFuture, long countDownInterval) { 2 mMillisInFuture = millisInFuture; 3 mCountdownInterval = countDownInterval; 4 } 5 6 public synchronized final CountDownTimer start() { 7 mCancelled = false; 8 if (mMillisInFuture <= 0) { 9 onFinish(); 10 return this; 11 } 12 //Pass the current start time + Count down the total time to calculate the end millisecond value 13 mStopTimeInFuture = SystemClock.elapsedRealtime() + mMillisInFuture; 14 //Then send a message Message to mHandler 15 mHandler.sendMessage(mHandler.obtainMessage(MSG)); 16 return this; 17 }
Code in mHandler:
1 // handles counting down 2 private Handler mHandler = new Handler() { 3 4 @Override 5 public void handleMessage(Message msg) { 6 7 synchronized (CountDownTimer.this) { 8 //If the user actively calls the cancel method, return 9 if (mCancelled) { 10 return; 11 } 12 13 //Step 1: first, judge the difference between the end time and the current time. 14 final long millisLeft = mStopTimeInFuture - SystemClock.elapsedRealtime(); 15 16 //Condition 1: if it is less than or equal to 0, it means it is over. 17 if (millisLeft <= 0) { 18 onFinish(); 19 } else if (millisLeft < mCountdownInterval) { 20 // no tick, just delay until done 21 //Condition 2: if the distance end time is less than the interval time value set by us 22 // Send one at this time millisLeft Delayed messages 23 sendMessageDelayed(obtainMessage(MSG), millisLeft); 24 } else { 25 long lastTickStart = SystemClock.elapsedRealtime(); 26 //Call our abstract method and call back the time value from the end of the distance as a parameter 27 onTick(millisLeft); 28 29 // take into account user's onTick taking time to execute 30 long delay = lastTickStart + mCountdownInterval - SystemClock.elapsedRealtime(); 31 32 // special case: user's onTick took more than interval to 33 // complete, skip to next interval 34 while (delay < 0) delay += mCountdownInterval; 35 36 //Send a delay, the time interval is set for us mCountdownInterval The news of 37 sendMessageDelayed(obtainMessage(MSG), delay); 38 } 39 } 40 } 41 };
Before the constructor is created, an internal Handler object is created, which is mainly used to send messages on a scheduled basis. When we call the start() method, we will send a Handler message, which will be processed in mHandler.
When the Handler receives the message, it will compare it with the set time interval value, and then send a delayed message.
public synchronized final void cancel() { mCancelled = true; mHandler.removeMessages(MSG); }