11-08-2010 12:59 PM
I'm writing a stopwatch application for BlackBerry
(which is similar to the BlackBerry built-in StopWatch).
There is a timer label displaying current time in the format MM
S:T (minutes, seconds, tenth of second).
The label is refresh each 100 millisecond with TimerTask.
The application works well and the time is display correctly, however, there are some moments the timer label is not updated at the predetermined interval (each 100 milliseconds).
The timer label pauses (not counting) for a while and continues counting (while still displays the time correctly)
My thought is the TimerTask is not executed to update the timer label during this pause.
Do you know why the app act this way, and how to fix it?
Below are the Thread to update the timer label:
public class ThreadUpdateTime extends Thread
{
private MyMainScreen myMainScreen;
private Timer updateTimerLabelTimer = new Timer();
public ThreadUpdateTime(MyMainScreen parent)
{
myMainScreen=parent;
}
public void run()
{
try {
updateTimerLabelTimer.schedule(new RecordTimer(myMainScreen), TIMER_DELAY, TIMER_INTERVAL);
} catch (Exception e) {
//put alert here
}
}
public void iStop()
{
updateTimerLabelTimer.cancel();
}
}
the timerTask:
public class RecordTimer extends TimerTask
{
private MyMainScreen myMainScreen;
public RecordTimer(MyMainScreen parent)
{
myMainScreen=parent;
}
public void run()
{
myMainScreen.iUpdateTimerLabel();
}
}
and the iUpdateTimerLabel method:
public void iUpdateTimerLabel()
{
//calculate : sign, sMin, sSec, sTenth
synchronized(Application.getEventLock())
{
lblSpotTime.setText(sign+sMin+":"+sSec+"."+sTenth+" ");
}
}
11-08-2010 01:05 PM - edited 11-08-2010 01:07 PM
Calling setText() on your LabelField too often is bad for performance and will lag your app because it calls updateLayout(). Try drawing the text manually and just calling invalidate() to repaint your custom Field. If you are already painting manually, then you shouldn't need to sync with event lock.
Also, you may want to schedule the task to run at a fixed rate so if it does lag then it won't try to play catch.
11-08-2010 01:26 PM
One of the approaches: check your scheduledExecutionTime() on each run() and just return if you are more than, say, half of your TIMER_INTERVAL behind.
11-08-2010 04:51 PM
Thanks mreed and arkadyz, I'll try each of your suggestions and report back to you guys.
11-08-2010 07:38 PM
You should use
updateTimerLabelTimer.scheduleAtFixedRate(something and something);
instead of schedule()
11-09-2010 12:53 AM - edited 11-09-2010 12:57 AM
Maybe this would help:
TimerTask TT;
(new Timer()).scheduleAtFixedRate(TT = new TimerTask() {
boolean first=false;
long lastime=System.currentTimeMillis();
public void run() {
if (first) {
first = false;
try {
Thread.currentThread().setPriority(Thread.MAX_PRIO RITY);
} catch (Exception e) { }
}
/* When it backs up and then races to catch up,
don't redraw display so much that it might cause
more lag, but also don't be so stingy that it
causes significant lag by omission. */
long currtime = System.currentTimeMillis();
if ((currtime - lastime) < 0.05)
label.setText( displayString(currtime) );
lastime = currtime;
}
} , 100, 100);
The point about setText being slower than drawing the text yourself is interesting, but for a mere 10 frames per second it should be no problem if you're doing little or nothing else.
Also, as amrishodiq points out, for this you'll want to use scheduleAtFixedRate instead of schedule(). The difference is, when the phone is interrupted by something higher-priority as inevitably happens, and there is a lag so that one or more calls to the timer are backed up, schedule() will resume triggering the timer at the proper rate, but drop the triggers that were missed. As long as the timer is reading the absolute time every time instead of incrementing a counter, that won't cause any cumulative error, but it will cause the display updating to skew freely by up to a tenth relative to when the actual time increments by a tenth. That's because after a lag, the TimerTask resumes triggering approximately every tenth, but not at the same delta-t to when the system time counts up a tenth. Basically it would be like a timer that's loosely frequency-locked with the system time, but is not phase-locked (thinking of the system time modulo the period as a sawtooth wave). So the counter would appear to count unevenly, like the second hand of the Windows clock ticks unevenly but the clock doesn't suffer any cumulative error in minutes.
By contrast, scheduleAtFixedRate will race to catch up when your thread can get priority again, so the counter would rip through a few counts (which the user wouldn't notice because they'd all display the correct current countdown time) and then be back on the intended rate, loosely phase-locked as well as loosely frequency-locked with the system time. The counting would then appear more even.
See what I mean? Either way you are going to see the counter hesitate when the phone is busy with something else, you can't take precedence over everything even when requesting high priority. But if you use schedule(), it will look more sloppy, whereas with scheduleAtFixedRate() it should look more solid.
11-11-2010 08:02 PM
There must be something else I'm missing. This is the complete source code of the app:tuyennguyen.ca/data/blackberry/stopwatch/StopWatch