Android CountDownTimer Case Summary

  • 2021-12-13 16:54:01
  • OfStack

Catalog 1. Overview 2. API3. Basic usage 4. Use notes

1. Overview

Countdown functions are often used in projects, such as time-limited snapping, mobile phone obtaining verification code and so on. The google official also helped us package a class: CountDownTimer, which made our development more convenient;

2. API

CountDownTimer is an abstract class with two abstract methods, and its API is very simple


public abstract void onTick(long millisUntilFinished);// This is a callback every time at a specified time interval, millisUntilFinished Remaining time in milliseconds 
public abstract void onFinish();// This is the callback at the end of the countdown 

When you use it, you only need to


new CountDownTimer(long millisInFuture, long countDownInterval)

//millisInFuture: Total length of countdown 

//countDownInterval : The interval between each time    The units are all milliseconds 

3. Basic usage

We look at the countdown of SMS verification code, click to get the verification code, and the countdown of 60s cannot be clicked


new CountDownTimer(60 * 1000, 1000) {
    @Override
    public void onFinish() {
        if (tvCode != null) {
            tvCode.setText(" Retrieve ");
            tvCodeWr.setTextColor(Color.parseColor("#E94715"));
            tvCode.setClickable(true);
            tvCode.setEnabled(true);
        }

        cancel();
    }

    @Override
    public void onTick(long millisUntilFinished) {
        if (tvCode != null) {
            tvCode.setClickable(false);
            tvCode.setEnabled(false);
            tvCode.setText(millisUntilFinished / 1000 + "s");
            tvCode.setTextColor(Color.parseColor("#999999"));
        }
    }
}.start();

Click on the button to get the verification code successfully, and then you can perform the above operations. The last 1 must be start, otherwise it will not be executed

4. Use caution

CountDownTimer is very simple to use, but there are many pits, so we should pay attention to avoid stepping on pits.

1. Null pointer: If cancle method is not called when activity or fragment is closed and destroyed, its onTick method will continue to execute. At this time, UI controls are all empty, so it is easy to null pointer if you don't pay attention to judgment

2. The time is not too accurate:

When we look at the source code of CountDownTimer, we can see that when executing the method of onTick, the time consumed when the program is executed here is subtracted from the source code of google, and we can see the rigor of google code here


final long millisLeft = mStopTimeInFuture - SystemClock.elapsedRealtime();

if (millisLeft <= 0) {
    onFinish();
} else if (millisLeft < mCountdownInterval) {
    // no tick, just delay until done
    sendMessageDelayed(obtainMessage(MSG), millisLeft);
}

Therefore, the countdown time from 1 is 59, which can be solved by adding a little time to the constructor. For example:


new CountDownTimer(60 * 1000+300, 1000)

3. Memory leak problem

First, let's look at the source code. The core code is as follows


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

It can be seen that the principle of CountDownTimer still uses Handler. Therefore, it is easy to cause memory leakage problems, When Activity or Fragment is closed and the countdown is not over, it will be executed directly in the background. Most of the time, we will update UI with countdown, and the controls all hold references to activity. If they are not released for a long time, it will cause memory leakage and even cause the null pointer problem mentioned in 1, so 1 should call cancle method when activity or fragment is destroyed.

I encapsulated this myself and wrote it as a tool class for reference:


public class TimeUtils {
    private String color;// You can modify the text color here 
    WeakReference<TextView> tvCodeWr;// Control soft reference to prevent memory leakage 
    private CountDownTimer timer;


    public TimeUtils(TextView tvCode, String color) {
        super();
        this.tvCodeWr = new WeakReference(tvCode);
        this.color = color;
    }
// This is the countdown execution method 
    public void RunTimer() {
        timer = new CountDownTimer(60 * 1000 - 1, 1000) {
            @Override
            public void onFinish() {
                if (tvCodeWr.get() != null) {
                    tvCodeWr.get().setText(" Retrieve ");
                    tvCodeWr.get().setTextColor(Color.parseColor(color));
                    tvCodeWr.get().setClickable(true);
                    tvCodeWr.get().setEnabled(true);
                }

                cancel();
            }

            @Override
            public void onTick(long millisUntilFinished) {
                if (tvCodeWr.get() != null) {
                    tvCodeWr.get().setClickable(false);
                    tvCodeWr.get().setEnabled(false);
                    tvCodeWr.get().setText(millisUntilFinished / 1000 + "s");
                    tvCodeWr.get().setTextColor(Color.parseColor("#999999"));
                }
            }
        }.start();
    }
// This method can be used in activity Or fragment Called when destroyed to prevent memory leakage 
    public void cancle() {
        if (timer != null) {
            timer.cancel();
            timer = null;
        }
    }
}

Related articles: