Timer Support
MIDlets may need to delay or schedule activities to be
performed at a later time. The MIDP Specification provides two mechanisms to enable
this:
-
Timers are used for time-based
events while a MIDlet is running. For example, a timer used to change the frame
being shown in an animation sequence. Timers are covered in this section.
-
Alarms provide a one-shot time for MIDlets to be
launched if they are not already running. Automatic launching can be convenient,
for example, to register the next appointment in a calendar. Alarm-based MIDlet
launching is covered in Section 17.1, "Alarm-Based
MIDlet Launch."
The MIDP Specification provides two classes: Timer and
TimerTask, that include functions for several types of timers. The
general setup of a timer is illustrated next. In order to define a task to run,
the programmer first defines a new timer task class by inheriting class
TimerTask. The code to be executed by the timer is defined in the
run method of the TimerTask subclass (see class
MyTask below). In order to set up the actual timer, instances of class
Timer and the new timer task class are created (objects
myTimer and myTask in this example), and the schedule
method of the timer object is then called with the task object and the
appropriate time values as parameters. In this example, the timer will start
executing in 10 milliseconds and will then repeat every 500 milliseconds.
class MyTask extends TimerTask {
public void run() {
...
}
}
...
myTimer = new Timer();
myTask = new MyTask();
myTimer.schedule(myTask, 10, 500);
There are two basic types of timers in MIDP: one-shot timers and repeating timers. The key difference between these two
types of timers is the scheduling approach. A one-shot timer executes the
specified task only once. A repeating timer continues executing the specified
task repeatedly at a specific interval.
Timers can be used by a MIDlet in both the Active and Paused
states, although use in the Paused state is
discouraged.
20.1.1 Using One-Shot
Timers
A one-shot timer can be set up to execute a task in two
different ways. In the first approach, a java.util.Date object is
passed into the schedule method of the Timer object. When the
time specified by the Date object occurs, the timer task is run. If the
Date passed in to the Timer object has already passed, then
the timer task is run immediately. The following code fragment illustrates a
timer whose task will be executed after 1000 milliseconds have passed after the
current time (that is, after the time when the timer scheduling took place):
myTimer.schedule(myTask,
new Date(System.currentTimeMillis() + 1000);
The second way to schedule a one-shot timer is by passing it a
long value that contains the number of milliseconds to wait before
executing the timer task. For instance, the following piece of code defines
another timer whose task will be executed after 1000 milliseconds have
passed:
myTimer.schedule(myTask, 1000);
20.1.2 Using Repeating
Timers
The second type of timer supported by MIDP is a repeating
timer. In this type of timer, the timer task is run repeatedly with a fixed
period until the timer is canceled. Like one-shot timers, repeating timers can
be set up to start executing the given task at a fixed time in the future (using
a Date object as a parameter) or after a delay of a given number of
milliseconds (using a long value as a parameter). In either case, the
timer scheduling methods take an additional long parameter that
indicates the repeat period of the timer. This period is interpreted either as a
fixed-delay or a fixed-rate period, depending on whether the scheduling
of the task is done using method schedule or
scheduleAtFixedRate, respectively.
myTimer.schedule(myTask, 10, 500); // Fixed-delay repeating timer
myTimer.scheduleAtFixedRate(myTask, 10, 500); // Fixed-rate timer
In the fixed-delay case, each execution of the timer task is
scheduled relative to the actual execution time of the previous timer task. If
an execution is delayed for any reason, such as garbage collection or other
background activity, subsequent executions will be delayed as well. This means
that the execution of a fixed-delay timer may drift if, for example, the system
is under a heavy execution load.
The fixed-rate case is different in that the execution of each
timer task is scheduled relative to the scheduled
execution time of the initial timer task. Thus, if an execution of a timer task
is delayed for any reason, two or more executions will occur in rapid succession
to "catch up" with the timer.
20.1.3 Sample Code
(TimerMIDlet.java)

The following example illustrates the use of timers and timer
tasks in more detail. In this example, two graphical gauges are created using
the javax.microedition.lcdui APIs, and these gauges are incremented or
decremented by timer tasks. (See the figure at left.) The MIDlet has two
abstract commands attached: OK and EXIT. If the OK
command is selected, then the state of the timers is changed from non-active to
active, or from active to non-active, depending on the current state of the
timers. The MIDlet will exit if the EXIT command is selected.
TimerMIDlet.java
package examples.timermidlet;
import java.lang.*;
import java.io.*;
import java.util.*;
import javax.microedition.lcdui.*;
import javax.microedition.midlet.*;
/**
* A simple class that shows an example of using a Timer and
* a TimerTask.
*
* This MIDlet creates two gauges. One gauge, gaugeOne,
* sets elements from low to high. The other, gaugeTwo,
* set elements from high to low. In effect, this has
* gaugeOne "going up," and gaugeTwo "going down."
*
* The two timers fire at different intervals.
*
* There are two commands on our form:
*
* OK: toggles whether the times are active or not.
* EXIT: exits the MIDlet.
*/
public class TimerMIDlet extends MIDlet implements CommandListener {
// number of elements in gauge
final private static int GAUGE_MAX = 10;
private boolean timersRunning; // tracks state of timers
private Display myDisplay; // handle to the display
private Gauge gaugeOne; // "going up" gauge
private Gauge gaugeTwo; // "going down" gauge
private Form myScreen; // form on which to
// place gauges
private Command cmdOK; // OK command
private Command cmdExit; // EXIT command
private Timer timer;
private MyTimerTask timerTaskOne;
private MyTimerTask timerTaskTwo;
/**
* Internal class that provides a TimerTask.
*/
private class MyTimerTask extends TimerTask {
private Gauge myGauge; // reference to gauge
private boolean goUp; // if true, go up
private int num; // number of times called
/**
* Public constructor: stores "direction" and a reference to
* a gauge.
*/
public MyTimerTask(Gauge g, boolean up) {
myGauge = g;
goUp = up;
}
/**
* As the timer fires, this method is invoked. Set gauge
* based on goUp
*/
public void run() {
num++;
myGauge.setValue(goUp ?
GAUGE_MAX -(num % GAUGE_MAX) :
num % GAUGE_MAX);
}
}
/**
* Public constructor: gets handle to display,
* creates form, gauges, and commands.
*/
public TimerMIDlet() {
myDisplay = Display.getDisplay(this);
myScreen = new Form("TimerMIDlet");
gaugeOne = new Gauge("Up Gauge",
false,
GAUGE_MAX,
0);
myScreen.append(gaugeOne);
gaugeTwo = new Gauge("Down Gauge",
false,
GAUGE_MAX,
GAUGE_MAX);
myScreen.append(gaugeTwo);
cmdOK = new Command("OK", Command.OK, 1);
cmdExit = new Command("Exit", Command.EXIT, 1);
myScreen.addCommand(cmdOK);
myScreen.addCommand(cmdExit);
myScreen.setCommandListener(this);
}
/**
* Changes the state of timers to/from active to/from
* not active.
*/
private void flipFlop() {
if (timersRunning) {
timerTaskOne.cancel();
timerTaskTwo.cancel();
timer.cancel();
timersRunning = false;
} else {
timer = new Timer();
timerTaskOne = new MyTimerTask(gaugeOne, false);
timerTaskTwo = new MyTimerTask(gaugeTwo, true);
timer.schedule(timerTaskOne, 0, 1000);
timer.schedule(timerTaskTwo, 0, 1500);
timersRunning = true;
}
}
/**
* Called by the system to start our MIDlet.
* @exception MIDletStateChangeException
*/
protected void startApp() throws MIDletStateChangeException {
myDisplay.setCurrent(myScreen);
flipFlop();
}
/**
* Called by the system to pause our MIDlet.
* No actions required by our MIDLet.
*/
protected void pauseApp() {}
/**
* Called by the system to end our MIDlet.
* No actions required by our MIDLet.
*/
protected void destroyApp(boolean unconditional) {}
/**
* Respond to command selections. Process two commands:
*
* OK: flip flop the timers to/from active
* EXIT: exit this MIDlet
*/
public void commandAction(Command c, Displayable d) {
if (c == cmdOK) {
flipFlop();
} else if (c == cmdExit) {
destroyApp(false);
notifyDestroyed();
}
}
}