3

I'm using a runnable in my Android app to update a countdown timer, as shown in the code below. It appears to work but I noticed my timer takes a few seconds longer than expected. For example, if it's supposed to count down 3 minutes, it takes 3 minutes and 5 seconds. I tried using a timer in a service to manage the countdown display in the main activity. The timer/service worked as expected.

Why doesn't runnable/postDelayed() run for the correct amount of time? Is postDelayed() timing reliable? The runnable decrements a variable then uses it to update an EditText with setText(). Does setText() take too long (a small fraction of a second), so the runnable really runs every 1.x seconds?

Handler handler = new Handler();
Runnable r = new Runnable() {
   public void run() {
      // decrement the time remaining and update the display
      handler.postDelayed(this, 1000);
   }
};
...
// start the runnable
handler.postDelayed(r, 1000);
sarnold
  • 102,305
  • 22
  • 181
  • 238
MikeU
  • 114
  • 1
  • 8
  • If you want the timer to be accurate, it is better to post a message from inside the runnable, than updating the UI directly. Ideally, of course, you should have the timer in a service as you mentioned. – Rajath Mar 26 '11 at 08:17
  • I see large variation in the time taken for postDelayed to fire. When the screen is *off* a 2 minute setting causes 6 - 14 minutes of delay. (Android 2.2, HTC Hero) – fadedbee Jun 12 '12 at 13:25

2 Answers2

2

Your code is kinda sorta designed to be inaccurate because you are not accounting for the time taken by the guts of the runnable. You might get improved results by doing something like

public void run(){  
    startTime = System.currentTimeMillis();  
    // compare expectedTime to startTime and compensate  
    // <guts of runnable goes here>
    // now wrap it up...
        delay = 1000 - (System.currentTimeMillis() - startTime);  
    if (delay < 0)  
        delay = 0;
    expectedTime = System.currentTimeMillies() + delay;
    handler.postDelayed(this, delay);  
}
George Freeman
  • 2,260
  • 1
  • 15
  • 22
  • I considered this kind of approach. The countdown finished on time, but the delay correction caused the timer to not display the second when a delay correction was needed. This behavior was not desired since it gave the appearance that the timer was not accurate. – MikeU Mar 30 '11 at 05:36
  • @MikeU Yes, it's true: this kind of approach might occasionally skip a beat. The answer to your question is: postDelayed()'s timing is *not* guaranteed--it's only approximate. If you need more accuracy, you do indeed need to try a more complex approach. – George Freeman Mar 30 '11 at 12:19
  • @MikeU Thanks for accepting my answer--made me think about your issue a little more :-) I have edited my answer to add one additional feature that might help: you might want to set a variable to hold the time at which you "expect" the next callback to occur, so you can detect and possibly compensate for weirdness. I would consider this the next step up in complexity--possibly useful but still manageable. Of course, you will need to initialize expectedTime to 0 in your constructor and ignore this value during the first call to the runnable. Good luck. – George Freeman Apr 17 '11 at 15:44
1

What about using CountDownTimer? I used this for same tasks several times and haven’t met this kind of problem.

Adorjan Princz
  • 11,708
  • 3
  • 34
  • 25