CountDownTimer source code analysis

Keywords: Android less xml encoding

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);
}

Posted by mattyj10 on Thu, 02 Apr 2020 06:11:51 -0700