Table of ContentsMIDP API OverviewUsing Timers

The MIDlet Application

The core of the MIDlet application is the MIDlet class. To create a MIDlet, you must derive your class from this abstract base class. Table 5.2 provides a list of the methods inherited from the MIDlet class.

Table 5.2. javax.microedition.MIDlet

Method

Description

Access to properties in the JAR and JAD files

String getAppProperty(String key)

Returns the property value associated with the string key in the JAR or JAD.

Application manager telling your MIDlet something

abstract void destroyApp(boolean unconditional)

The Application Manager calls this method to give you a chance to do something (such as save state and release resources) before your application is closed.

abstract void pauseApp()

The Application Manager calls this method on your MIDlet when the user has paused the game.

abstract void startApp()

The Application Manager calls this method to tell you that the user wants the game to start again.

Your MIDlet telling the Application Manager something

abstract void notifyDestroyed()

If your player decides to exit the game, you can call this method to inform the Application Manager.

abstract void notifyPaused()

Call this method to tell the Application Manager that the player has paused the MIDlet.

abstract void resumeRequest()

Call this method to tell the Application Manager that the MIDlet wants to start again (after being paused).


As you can see in Table 5.2, the MIDlet class (apart from the getAppProperty method) is a little weird. That's because you need a little understanding of exactly how the MIDlet fits into the world and, more importantly, the rules it has to live by when under the control of the Application Manager before you can see what the MIDlet class is really doing.

The application manager's role is to control MIDlets. Think of it as something like the Master Control Program from TRON; it dictates what's going on, so anytime you want to do anything regarding the state of the application, you need to contact the Application Manager (AM) and let it know what's going on. And likewise, the AM will be kind enough to let you know when these state-change events occur. Happily, though, I don't think it will go power crazy and attempt to take over the world ...I hope.

Essentially, there are only two states in which your application can practically exist paused or running. As you can see in Figure 5.1, your MIDlet will be constructed and then placed in the paused state by default. When the AM thinks it's readymaybe it has to go get a coffee firstit will call the startApp method to notify you that the MIDlet is moving into the running state.

Figure 5.1. The MIDlet class serves as the gateway for communicating application-state changes to the application manager and vice versa.

graphic/05fig01.gif


When your MIDlet is running, you can pause it at any time. If the user does this, say by moving to another application, the AM will immediately call your MIDlet's pauseApp method. This gives you a last chance to let go of heavy resources. (Keep in mind that you might be paused indefinitely.) When the user wants to start again, the AM will then call yep, you guessed itthe startApp method again. This can go on ad infinitum.

Destruction of your MIDlet works the same way; if the user elects to abandon you like a battery-operated toy on the day after Christmas, then the AM is kind enough to call your MIDlet's destroyApp method to let you clean up things.

Of course, not all of these events come from the application manager; quite often the user will choose the Pause or Exit command in your game. You're then obliged to tell the application manager about this event. The two methods you use in this case areyou guessed itnotifyDestroyed and notifyPaused.

To better understand how all this interaction occurs, take a look at some code.

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;

/**
 * A MIDlet which demonstrates the lifecycle of a MIDlet
 * @author Martin J. Wells
 */
public class LifecycleTest extends javax.microedition.midlet.MIDlet
         implements CommandListener
{
   private Form form;
   private Command quit;
   private boolean forceExit = false;

   /**
    * Constructor for the MIDlet which creates a simple Form, adds some text and
    * an exit command. When called this method will also write a message to the
    * console. This is used to demonstrate the sequence of events in a MIDlet's
    * lifecycle.
    */
   public LifecycleTest()
   {
      System.out.println("Constructor called.");

      form = new Form("Life, Jim.");
      form.append("But not as we know it.");
      form.setCommandListener(this);

      // Create and add two commands to change the MIDlet state 
      quit = new Command("Quit", Command.SCREEN, 1);
      form.addCommand(quit);
   }

   /**
    * Called by the Application Manager when the MIDlet is starting or resuming
    * after being paused. In this example it acquires the current Display object
    * and uses it to set the Form object created in the MIDlet constructor as

    * the active Screen to display. Also displays a message on the console when
    * called to demonstrate when this method is called in the MIDlet lifecycle.
    * @throws MIDletStateChangeException
    */
   protected void startApp() throws MIDletStateChangeException
   {
      System.out.println("startApp() called.");
      Display.getDisplay(this).setCurrent(form);
   }

   /**
    * Called by the MID's Application Manager to pause the MIDlet. A good
    * example of this is when the user receives an incoming phone call whilst
    * playing your game. When they're done the Application Manager will call
    * startApp to resume. For this example we output a message on the console
    * to indicate when this method is called in the MIDlet's lifecycle.
    */
   protected void pauseApp()
   {
      System.out.println("pauseApp() called.");
   }

   /**
    * Called by the MID's Application Manager when the MIDlet is about to
    * be destroyed (removed from memory). You should take this as an opportunity
    * to clear up any resources and save the game. For this example we output a
    * message on the console to indicate when this method is called in the
    * MIDlet lifecycle.
    * @param unconditional if false you have the option of throwing a
    * MIDletStateChangeException to abort the destruction process.
    * @throws MIDletStateChangeException
    */
   protected void destroyApp(boolean unconditional) throws MIDletStateChangeException
   { 
      System.out.println("destroyApp(" + unconditional + ") called.");

      if (!unconditional)
      {
         // we go through once using unconditional, next time it's forced.
         forceExit = true;
      }
   }

   /**
    * The CommandListener interface method called when the user executes a
    * Command, in this case it can only be the quit command we created in the
    * constructor and added to the Form. We also output a console message when
    * this method is called.
    * @param command the command that was triggered
    * @param displayable the displayable on which the event occurred
    */
   public void commandAction(Command command, Displayable displayable)
   {
      System.out.println("commandAction(" + command + ", " + displayable +
                           ") called.");
      try
      {
         if (command == quit)
         {
            destroyApp(forceExit);
            notifyDestroyed();
         }
      }

      catch (MIDletStateChangeException me)
      {
         System.out.println(me + " caught.");
      }
   }
}

This code will display a simple text message and a Quit command. When the user hits the Quit command, a Really? command will take its place (see Figure 5.2). Executing this command will subsequently cause the MIDlet to shut down.

Figure 5.2. The output from our test MIDlet demonstrates the application termination process.

graphic/05fig02.gif


The System.out.println lines in this code will also generate the following console output:

Constructor called.
startApp() called.
commandAction(Quit) called.
destroyApp(false) called.
javax.microedition.midlet.MIDletStateChangeException caught.
commandAction(Really?) called.
destroyApp(true) called.

Take a more detailed look at exactly how this all works. The first two lines are the standard package imports for the MIDP application and user-interface packagespretty standard stuff. The next line is the class declaration.

public class LifecycleTest extends javax.microedition.midlet.MIDlet
         implements CommandListener

Here you extend from the base javax.microedition.midlet.MIDlet class, which gives you access to the core MIDlet application functionality, and implement the CommandListener interface, which lets you "listen" to events generated by commands. (You'll learn more about the ins and outs of this interface later.)

The next section includes the field declarations for the user-interface objects that you need in the MIDlet. There are display and form objects along with two commands (think of them like buttons for now). The constructor then initializes these objects in the correct order and adds them to the display. Again, I don't want to dwell too much on the user-interface aspects of this code; I'll cover that in much more detail later in the chapter.

However, there are a few things to note about how this MIDlet constructs the display objects. First, note that you initialize these in the constructor, not in startApp. This further demonstrates the role of the startApp call. Think of it more as a resume method than as a type of initialization procedure. You will get one call when the application starts, and then after that a subsequent call when a MIDlet is being resumed after it was stopped (say when the user had to answer a call). Because of this you shouldn't use startApp to initialize objects that exist across pauses in execution (in your case, that is all the display objects).

In your case, the startApp method just calls

System.out.println("startApp() called.");
display.setCurrent(form);

This displays a console message, and then sets the previously initialized display to be the current one. The next section is the pauseApp method.

protected void pauseApp()
{
   System.out.println("pauseApp() called.");
}

Doesn't do much, I know; however, in later sections you'll see how the pauseApp method can clear out any resources you really don't need before your MIDlet is put on hold. It's good practice to free as many resources as is practical. It is quite acceptable to spend a little time later reinitializing these resourcesthe user will expect a delay when resuming the game.

From the console output, you also might have noticed that there is no output corresponding to the pauseApp method, which means the Application Manager never called it. This is interesting given that the MIDP specifications state that a MIDlet begins in the paused state, which is why the first post-construction call is the startApp method. You never see an initial call to pauseApp because the Application Manager only calls these methods before you enter a new state. However, since you began in that state when the MIDlet started, the method to notify you of the transition to that state doesn't ever need to be called.

The destroyApp method is a little more interesting.

protected void destroyApp(boolean unconditional) throws MIDletStateChangeException
{
   System.out.println("destroyApp(" + unconditional + ") called.");

   if (!unconditional)
       throw new MIDletStateChangeException();
}

This code shows you more about exactly what you can do with the destroyApp method. As with pauseApp, keep in mind that this is the Application Manager (or your MIDlet) asking whether it can exit. The keyword here is askhence you can say no by throwing a hissy fit, also known as a MIDletStateChangeException. However, you won't always have an option, indicated by the Boolean flag passed into the method (the unconditional parameter). If this is true, then Kansas is saying bye-bye, and you don't have a choice in the matter.

If you skip down to the commandAction method, you can see how all of this starts to fit together.

public void commandAction(Command command, Displayable displayable)
{
   System.out.println("commandAction(" + command.getLabel() + ") called.");
   try
   {
      if (command == quit)
         destroyApp(false);

      if (command == really)
      {
         destroyApp(true);
         notifyDestroyed();
      }
   }

   catch (MIDletStateChangeException me)
   {
      System.out.println(me + " caught.");
      form.removeCommand(quit);
      form.addCommand(really);
   }
}

First, note that you wrap the command processing code in a try block so you can catch the hissy fit when it happens. This wrapped code handles events coming from the Quit and Really? commands. As you can see, when the user hits the Quit command, all you do is call the destroyApp method with the unconditional flag set to false. The destroyApp code then throws the exception via:

if (!unconditional)
    throw new MIDletStateChangeException();

The catch (MIDletStateChangeException me) block in the commandAction method then catches the exception and makes the change to the user interface by removing the Quit command and adding the Really? command. When you subsequently hit the Really? command, the commandAction method executes the code.

if (command == really)
{
   destroyApp(true);
   notifyDestroyed();
}

This time the destroyApp method doesn't get a choicewhich in your MIDlet's case means it doesn't do anything. The final call to notifyDestroyed then tells the Application Manager to shut down the MIDlet.

Thankfully, the basics of MIDlet application lifecycles (and a bit of user interface) are out of the way. Now I want to leave Kansas for a little while and look at how to get you some rhythm (don't ask how I know that you don't have any) through the use of Timers.

    Table of ContentsMIDP API OverviewUsing Timers