| [ directory ] |
|
7.1 MIDletsIn MIDP, the basic unit of execution is a MIDlet. A MIDlet is a class that extends the class javax.microedition.midlet.MIDlet and implements a few methods梚ncluding startApp, pauseApp, and destroyApp梩o define the key behavior of the application. As an example of programming with the MIDP application model, consider the following program that implements one of the simplest MIDlets possible: the canonical "Hello World" application.
package examples;
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
public class HelloWorld extends MIDlet implements CommandListener {
private Command exitCommand;
private TextBox tb;
// Constructor of the HelloWorld class
public HelloWorld() {
// Create "Exit" Command so the user can exit the MIDlet
exitCommand = new Command("Exit", Command.EXIT, 1);
// Create TextBox in which the output will be displayed
tb = new TextBox("Hello MIDlet", "Hello, World!", 15, 0);
// Add the Command and set the CommandListener
tb.addCommand(exitCommand);
tb.setCommandListener(this);
// Set our TextBox as the current screen
Display.getDisplay(this).setCurrent(tb);
}
// Called by the system when the MIDlet is started
// for the first time, and when it is resumed after
// being paused.
protected void startApp() {}
// Called by the system when the MIDlet is paused.
// In this application, there is no need to do anything
// when we are paused.
protected void pauseApp() {}
// Called when the MIDlet is destroyed. In this case,
// there is no need to do anything when we are destroyed.
protected void destroyApp(boolean force) {}
// This method is called automatically by the system
// in response to a user activating one of the Commands.
public void commandAction(Command c, Displayable d) {
// Upon receiving the "Exit" Command, destroy ourselves
if (c == exitCommand) {
destroyApp(false);
notifyDestroyed();
}
}
}
The output of this simple MIDlet, when run on a MIDP device emulator, is shown in the figure on the right. In the figure, the MIDlet's startApp method has just completed its execution. Pressing the "Exit" button on the phone's keypad would trigger the delivery of the exitCommand to the commandAction method which invokes the destroyApp and notifyDestroyed methods. In this example, there are a few key things that are common to all MIDlets, no matter how simple or complex the applications are. First, HelloWorld extends the class javax.microedition.midlet.MIDlet. Second, like every Java class, a MIDlet can have a constructor. In the MIDP application model, the system (see Section 19.2, "MIDP System Software") calls the no-argument constructor of a MIDlet exactly once to instantiate the MIDlet. The functions that need to be performed in the constructor depends on how the MIDlet is written, but in general, any operations that must be performed only once when the application is launched should be placed in the constructor. If no such functionality is required by the MIDlet, then there is no need to provide a constructor. Care should be taken in the constructor to catch any exceptions and handle them gracefully because an uncaught exception will prevent the MIDlet from being created and the application will not be able to run. Third, the application's MIDlet class must implement the abstract lifecycle methods of class javax.microedition.midlet.MIDlet so the application can respond to state changes. These methods are: startApp, pauseApp, and destroyApp. The startApp method is used for starting or restarting a MIDlet. The pauseApp method is called by the system to ask a MIDlet to "pause." The destroyApp method is called by the system when the MIDlet is about to be destroyed; it can also be called indirectly by the MIDlet itself in order to clean up before exiting. Taken as a whole, the startApp, pauseApp, and destroyApp methods represent a state machine that controls the life cycle of the MIDlet. This life cycle, as well as the recommended practices for defining the startApp, pauseApp, and destroyApp methods, are described in more detail in Section 7.1.1, "MIDlet States." Fourth, the application also implements the CommandListener interface (see method CommandAction) so that the application can respond to user actions. This interface, along with the TextBox, Command, and Display classes, is part of the javax.microedition.lcdui package that will be discussed in detail later in this book. 7.1.1 MIDlet StatesMIDlets have a well-defined life cycle. A MIDlet is typically started (for example, when the user launches it from a screen of applications available to be run), it interacts with the user through its graphical user interface, and is then stopped (for example, when the user chooses a button labelled "Exit"). In general, during the lifetime of a MIDlet, it may be in one of three distinct states, with well-defined rules that govern the transitions between these states (see Figure 7.1):
Figure 7.1. MIDlet states and state transitions
The MIDlet state transitions can be triggered either by the MIDP system itself or by requests from the application. Below we summarize how the MIDP system initiates these state transitions by calling methods on the MIDlet. Note that with the exception of startApp, which completes the state change to Active upon the origination of the call, all other state changes complete after the associated methods return to the caller. The MIDlet's constructor is invoked to create an instance of the MIDlet. A MIDlet is in the Paused state when it has just been constructed and has not yet entered its startApp method. If defined, a constructor must be public or protected and have no arguments. The MIDP system calls the constructor exactly once to instantiate the MIDlet. (See Section 19.2, "MIDP System Software" for an explanation of the MIDP system.) In general, MIDlet programmers should use the constructor for any initialization operations that must be performed only once and can be performed when the application is launched. If a MIDlet has no such operations, then the MIDlet programmer need not provide a constructor. The initialization done within the constructor should be brief. Lengthy delays caused by long operations such as network access should be performed in the background or delayed until the user can be informed appropriately. The startApp method is called by the system to move a MIDlet into the Active state for the first time and also when a MIDlet is being resumed from the Paused state. When a MIDlet enters its startApp method, it is in the Active state. An important note: Beginning MIDlet programmers are often tempted to treat startApp as equivalent to the main method, and thus combine MIDlet initialization and main-line processing in the startApp method. In general this is not a good idea, since the startApp method may be called more than once: one time initially, and then once per each transition from Paused to Active state. The pauseApp method is called to move the MIDlet into the Paused state. During the normal operation of a mobile device, the system might run into situations where it is necessary to suspend or pause some of the MIDlets on the device. The most common example is when a device is running low on memory. In order to reclaim memory, the system may call the pauseApp method of all the Active MIDlets. When the pauseApp method of a MIDlet is called, the MIDlet should release as much of its resources as possible and become quiescent. Note that the system does not actually try to force any specific behavior on a MIDlet in a Paused state, so it is possible for an ill-behaved MIDlet to ignore this request. However, if such a situation occurs, the system may forcibly terminate the MIDlet or even the virtual machine itself, especially in a low-memory situation. The destroyApp method is the normal way the system terminates a MIDlet. The destroyApp method has one boolean parameter that indicates whether or not the request is unconditional. If the request is not unconditional (that is, the boolean parameter is false), then the MIDlet may request a "stay of execution" by throwing a MIDletStateChangeException. In this case, if the system is able to honor the request, the MIDlet may continue in its current state (Paused or Active). If, on the other hand, the request is unconditional, then the MIDlet should give up its resources, save any persistent data it might be caching, and return. Upon normal return from this method, the MIDlet enters the Destroyed state and can be reclaimed by the system. While the MIDP system is the primary instigator of MIDlet state changes, a MIDlet application programmer can also request state changes using the resumeRequest, notifyPaused, and notifyDestroyed methods in the MIDlet class. The resumeRequest method can be called by a Paused MIDlet to indicate that it wishes to reenter the Active state. The primary scenario for this call is when a Paused MIDlet handles a timer expiration and needs to resume processing. The notifyPaused method is provided to allow a MIDlet to signal to the system that it has voluntarily entered the Paused state (that is, it has released its resources and is now quiescent). An example use case for this call is a time-based MIDlet that sets timers and has nothing to do until those timers expire. The notifyDestroyed method can be called by the MIDlet to tell the system that the MIDlet has released all its resources and has saved any cached data to persistent storage. Note that the destroyApp method of a MIDlet will not be called by the system as a result of invoking this method. The MIDlet may reasonably call destroyApp itself to do the same cleanup as if the system called it. 7.1.2 Using the startApp, pauseApp, and destroyApp MethodsUsing the startApp methodThe startApp method is used for starting or restarting a MIDlet. This method may be called by the system under different circumstances (see Section 7.1.1, "MIDlet States"), but its purpose is to acquire or reacquire resources needed by the MIDlet and to prepare the MIDlet to handle events such as user input and timers. Note that the startApp method may be called more than once梠nce to start execution for the first time and again for every time the MIDlet is "resumed"; therefore, a startApp method should be written so that it can be called under these different circumstances. The startApp method can "fail" in two ways: with a transient failure or a non-transient failure. A transient failure is one that might have to do with a certain point in time (such as a lack of resources, network connectivity, and so forth). Since this type of error may not be fatal, a MIDlet can tell the system that it would like to try to initialize later by throwing a javax.microedition.midlet.MIDletStateChangeException. A non-transient failure is one that is caused by any other unexpected and uncaught runtime error. In this case, if the runtime error is not caught by the startApp method, it is propagated back to the system that will destroy the MIDlet immediately and call the MIDlet's destroyApp method. A robust startApp method should distinguish between transient and non-transient exceptions and respond accordingly, as illustrated in the following code fragment:
void startApp() throws MIDletStateChangeException {
CommConnection port = null;
try {
port = (CommConnection)Connector.open(...);
...
} catch (IOException ioe) {
// Transient failure: could not connect to the serial port
throw new MIDletStateChangeException(
"Serial port not available");
} catch (Exception e) {
// Non-transient failure: can either catch this and
// exit here, or let the exception propagate back to
// the system
destroyApp(true);
notifyDestroyed();
}
}
Using the pauseApp methodThe pauseApp method is called by the system to ask a MIDlet to "pause." The definition of what pause means is discussed in Section 7.1.1, "MIDlet States," but in general, the pauseApp method works in conjunction with the startApp method to release as many resources as possible so that there is more memory and/or resources available to other MIDlets or native applications. The use of the pauseApp method is illustrated in the following code fragment:
int[] runTimeArray = null;
void startApp() {
// Coming from a paused state; initialize data
runTimeArray = new int[200];
...
}
void pauseApp() {
// Set runTimeArray to null so that it is
// a candidate for garbage collection
runTimeArray = null;
...
}
Using the destroyApp methodThe last method that a MIDlet must implement is the destroyApp method. This method is called by the system when the MIDlet is about to be destroyed; it can also be called indirectly by the MIDlet itself in order to clean up before exiting. In either case, the destroyApp method should be written so that it performs all the necessary cleanup operations to release all the resources (that is, close the graphical user interface components, network connections, and database records) that the application had allocated during its execution. The following code fragment provides a simple example:
RecordStore rs = null;
void startApp() {
...
rs = RecordStore.openRecordStore(...);
...
}
void destroyApp(boolean force) {
if (rs != null) {
// Make sure all records are written to storage
// and close the record store
...
}
}
7.1.3 Application Lifecycle and the User Interface APISince the user interface components play a key role in a MIDP application, the initialization of the user interface components is one of the first tasks for the application when a MIDlet is instantiated. The application management software of a MIDP system assumes that the application is well-behaved with respect to the MIDlet events. The paragraphs below summarize what a well-behaved MIDlet is expected to do when each of its lifecycle methods is called. The constructor initializes the application state. The method can access the Display for the MIDlet by calling the Display.getDisplay method. It can create screens and objects needed when the application is started. It does not have access to the user interface, so information or alerts cannot be shown. The method can set the first screen to be shown to the user. The initialization performed should be brief; lengthy delays caused by long operations such as network access should be performed in the background or delayed until the user can be informed appropriately. The application manager calls the startApp method to notify the MIDlet that it has been started (or resumed after being paused) and makes the screen set with Display.setCurrent visible when startApp returns. Note that startApp can be called several times if pauseApp is called in between. This means that objects or resources freed by pauseApp might need to be recreated. When the pauseApp method is called the application should release any unneeded resources that can be re-initialized if the application is restarted. The application should pause its threads unless they are needed for background activities. Also, if the application should restart with another screen when the application is reactivated, the new screen should be set with Display.setCurrent. Since CLDC does not provide guaranteed real-time behavior, the system is not required to use the Paused state to manage the interaction of the real-time portions of the phone and the Java environment. Consider, for example, the case where the virtual machine is running on a cellular phone, and the phone receives a call. In this scenario, the real-time operating system on the phone might suspend the virtual machine altogether rather than cycle the MIDlets to the Paused state. When the destroyApp method is called the application's user interface will no longer be displayed. Still, the application should close all active resources, stop any active threads, and unregister or free any objects that would not be freed by a normal garbage collection cycle. |
| [ directory ] |
|