Android: CountDownTimer skips last onTick()!

ProgrammingMakesMeQQ picture ProgrammingMakesMeQQ · Jan 13, 2012 · Viewed 32.8k times · Source

Code:

public class SMH extends Activity {  

    public void onCreate(Bundle b) {  
        super.onCreate(b);  
        setContentView(R.layout.main);  

        TextView tv = (TextView) findViewById(R.id.tv);  

        new CountDownTimer(10000, 2000) {  
            public void onTick(long m) {  
               long sec = m/1000+1;  
               tv.append(sec+" seconds remain\n");  
            }  
            public void onFinish() {  
               tv.append("Done!");  
            }  
        }.start();  
   }

Output:
10 seconds remain
8 seconds remain
6 seconds remain
4 seconds remain
Done!

Problem:

How do I get it to show "2 seconds remain"? The time elapsed is indeed 10 seconds, but the last onTick() never happens. If I change the second parameter from 2000 to 1000, then this is the output:

10 seconds remain
9 seconds remain
8 seconds remain
7 seconds remain
6 seconds remain
5 seconds remain
4 seconds remain
3 seconds remain
2 seconds remain
Done!

So you see, it seems to be skipping that last onTick() call. And btw, the XML file is basically the default main.xml with the TextView assigned the id tv and the text set to "".

Answer

Nantoka picture Nantoka · Sep 5, 2012

I checked the source code of CountDownTimer. The "missing tick" comes from a special feature of CountDownTimer that I have not yet seen being documented elsewhere:

At the start of every tick, before onTick() is called, the remaining time until the end of the countdown is calculated. If this time is smaller than the countdown time interval, onTick is not called anymore. Instead only the next tick (where the onFinish() method will be called) is scheduled.

Given the fact that hardware clocks are not always super precise, that there may be other processes in the background that delay the thread running CountDownTimer plus that Android itself will probably create a small delay when calling the message handler of CountDownTimer it is more than likely that the call for the last tick before the end of the count down will be at least one millisecond late and therefore onTick() will not be called.

For my application I solved this problem simply by making the tick intervals "slightly" smaller (500 ms)

    myCountDownTimer = new CountDownTimer(countDownTime, intervalTime - 500) {
                                   ...
    }

and I could leave my code just as it is. For applications where the length of the interval time is critical, the other solutions posted here are probably the best.