ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

The Mobile Information Device Profile and MIDlets, Part 4
Pages: 1, 2, 3, 4

Running a MIDlet

At this stage, the JAR file has not been created, but you can nevertheless test the MIDlet suite by selecting an appropriate target device on the KToolbar main window and pressing the Run button. This loads the MIDlet classes, its resources, and any associated libraries from the classes, res, and lib subdirectories. If you select the default gray phone and press the Run button, the emulator starts and displays the list of MIDlets in this suite, as shown in Figure 3-8.

Screen shot.
Figure 3-8. The Wireless Toolkit emulator.

When the MIDlet suite is loaded, the device's application management software displays a list of the MIDlets that it contains and allows you to select the one you want to run. In this case, even though the suite contains only one MIDlet, the list is still displayed, as shown in Figure 3-8. Given the current lack of security for MIDlets imported from external sources, it would be dangerous for the device to run a MIDlet automatically, and, by giving the device user the chance to choose a MIDlet, it allows him the opportunity to decide not to run any of the MIDlets if, for any reason, they are thought to be a security risk or otherwise unsuitable. It is not obvious, though, on what basis such a decision would be made, since the user will see only the MIDlet names at this stage, but requiring the user to confirm that a MIDlet should be run transfers the ultimate responsibility to the user. In this case, the device displays the MIDlet name and its icon (the exclamation mark) as taken from the MIDlet-1 attribute in the manifest file. The device is not obliged to display an icon, and it may use its own icon in preference to the one specified in the manifest.



When you run the MIDlet suite this way, the Wireless Toolkit compiles the source code with the option set to save debugging information in the class files, and it does not create a JAR file. If you want to create a JAR, you can do so by selecting the Package item from the Project menu. This rebuilds all the class files without debugging enabled, which reduces the size of the class files, a measure intended to keep the time required to download the JAR to a cell phone or PDA as small as possible. It also extracts the content of any JARs or ZIP files it finds in the lib subdirectory and includes them in the MIDlet JAR, after running the preverifier over any class files that it finds in these archives. The JAR can be used, along with the JAD file, to distribute the MIDlet suite for installation into a device over a network, as will be shown in the later section "Delivery and Installation of MIDlets.

Further information on the Wireless Toolkit and other MIDlet development environments can be found in Chapter 9.

A Simple MIDlet

Now let's look at the implementation of the ExampleMIDlet class you have just built and packaged with the Wireless Toolkit. This simple MIDlet demonstrates the lifecycle methods that were described in the earlier section "MIDlet Execution Environment and Lifecycle, and it also illustrates how the MIDlet's foreground activity interacts with background threads, as well as how to create and use timers. The code for this example in shown in Example 3-1. For clarity, the timer-related code has not been included in the code listing; you'll see how that works when timers are discussed later in this chapter.

Example 3-1: A Simple MIDlet

package ora.ch3;
 
import java.util.Timer;
import java.util.TimerTask;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;
 
public class ExampleMIDlet extends MIDlet {
    
    // Flag to indicate first call to startApp
    private boolean started = false;
    
    // Background thread
    private Thread thread;
    
    // Timer interval
    private int timerInterval;
    
    // Timer
    private Timer timer;
    
    // Task to run via the timer
    private TimerTask task;
    
    // Required public constructor. Can be omitted if nothing to do and no
    // other constructors are created.
    public ExampleMIDlet(  ) {
        System.out.println("Constructor executed");
        
       // Get the timer interval from the manifest or JAD file.
        String interval = getAppProperty("Timer-Interval");
        timerInterval = Integer.parseInt(interval); 
        System.out.println("Timer interval is " + interval);
    }
        
    protected void startApp(  ) throws MIDletStateChangeException {
        if (!started) {
            // First invocation. Create and start a timer.
            started = true;            
            System.out.println("startApp called for the first time");
            startTimer(  );
        } else {
            // Resumed after pausing. 
            System.out.println("startApp called following pause");
        }
        
        // In all cases, start a background thread.
        synchronized (this) {
            if (thread == null) {
                thread = new Thread(  ) {
                    public void run(  ) {
                        System.out.println("Thread running");
                        while (thread == this) {
                            try {
                                Thread.sleep(1000);
                                System.out.println("Thread still active");
                            } catch (InterruptedException ex) {
                            }
                        }
                        System.out.println("Thread terminating");
                    }
                };
            }
        };
         thread.start(  )
    }
 
    protected void pauseApp(  ) {
        // Called from the timer task to do whatever is necessary to pause
        // the MIDlet.
        // Tell the background thread to stop.
        System.out.println("pauseApp called.");
        synchronized (this) {
            if (thread != null) {
                thread = null;
            }
        }
    }
 
    protected void destroyApp(boolean unconditional) 
                            throws MIDletStateChangeException {
        // Called to destroy the MIDlet.
        System.out.println("destroyApp called - unconditional = " 
                            + unconditional);
        if (thread != null) {
            Thread bgThread = thread;
            thread = null;      // Signal thread to die
            try {
                bgThread.join(  );
            } catch (InterruptedException ex) {
            }
        }
        stopTimer(  );
    }
    
    // Timer code not shown here
}

This simple MIDlet does two things:

  • Starts a background thread that writes a message to standard output every second so that you can see that the MIDlet is active
  • Starts a timer that periodically pauses the MIDlet if it is active and makes it active again if it is paused

The code listing shows the implementation of the MIDlet's constructor and its startApp( ), pauseApp( ) and destroyApp( ) methods. A MIDlet is not required to do anything in its constructor and may instead defer initialization until the startApp( ) method is executed. In this example, the constructor prints a message so that you can see when it is being executed. It also performs the more useful function of getting the interval for the timer that will be used to change the MIDlet's state. It is appropriate to put this code in the constructor because this value needs to be set only once. The timer value is obtained from the Timer-Interval attribute that was specified in the settings dialog of the Wireless Toolkit and subsequently written to the JAD file. Here is what the JAD file created for this MIDlet suite actually looks like:

MIDlet-1: ExampleMIDlet, /ora/ch3/icon.png, ora.ch3.ExampleMIDlet
MIDlet-Jar-Size: 100
MIDlet-Jar-URL: Chapter3.jar
MIDlet-Name: Chapter3
MIDlet-Vendor: J2ME in a Nutshell
MIDlet-Version: 1.0
Timer-Interval: 3000

A MIDlet can read the values of its attributes using the following method from the MIDlet class:

public final String getAppProperty(String name);

This method looks for an attribute with the given name; it looks first in the JAD file, and then, if it was not found there, in the manifest file. Attributes names are case-sensitive and scoped to the MIDlet suite, so every MIDlet in the suite has access to the same set of attributes. The getAppProperty( ) method can be used to retrieve any attributes in the JAD file or the manifest, so the following line of code returns the name of the MIDlet's suite, in this case Chapter3:

String suiteName = geAppProperty("MIDlet-Name");

The timer interval for this MIDlet is obtained as follows:

String interval = getAppProperty("Timer-Interval");
timerInterval = Integer.parseInt(interval);

Once the value in the form of a string has been retrieved, the next step is to convert it to an integer by calling the Integer parseInt( ) method. If the Timer-Interval attribute is not included in the JAD file or manifest (or if its name is misspelled), getAppProperty( ) returns null, and the parseInt( ) method throws an exception. A similar thing happens if the attribute value is not a valid integer. Notice that the constructor does not bother to catch either of these exceptions. The main reason for catching an exception is to display some meaningful information to the user and possibly allow recovery, but, strictly speaking, the MIDlet is not allowed to use the user interface in the constructor, so attempting to post a message would not necessarily work. The most appropriate thing to do in a real MIDlet is to install a default value for the timer interval and arrange to notify the user from the startApp( ) method, when access to the user interface is possible. In this simple example, we allow the exception to be thrown out of the constructor, which causes the MIDlet to be destroyed. Additionally, the version of MIDP in the Wireless Toolkit does, in fact, display the exception on the screen, but vendor implementations are not bound to do so.

Pages: 1, 2, 3, 4

Next Pagearrow