September 23, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

Handling Life-Cycle Issues with the MIDlet User Interface

  • February 26, 2008
  • By Richard G. Baldwin
  • Send Email »
  • More Articles »

Java Programming Notes # 2578


Preface

This is one in a series of tutorial lessons designed to teach you how to write programs using the Sun Java Wireless Toolkit for CLDC. The first lesson was titled Getting Started with MIDlets and the Sun Java Wireless Toolkit for CLDC. This is the second part of a lesson titled Introduction to the MIDlet User Interface (see Resources).

A MIDlet development framework

For this lesson, you will need the MIDlet development framework that I provided in the earlier lesson titled Capturing Output Produced by Programs Running in a Child Process (see Resources).

What you learned in the previous part

In the previous part of this lesson, you learned:

  • The fundamentals of user interfaces for MIDlets.
  • How to instantiate user interface components.
  • How to cause them to become visible on the cell phone screen.
  • The difference between a Screen and a Display.
  • About restrictive constraints and modifier flags.
  • About the MIDlet user interface class hierarchy, and
  • About the methods of the various classes that can be used to manipulate user input and output.

What you will learn in this part

The sample programs used in the previous part of this lesson (see Resources) were about as simple as I could make them. Those programs made no serious effort to honor the life cycle requirements for MIDlets. In this part, you will learn to program MIDlet user interfaces that minimize the MIDlet's memory footprint while the MIDlet is paused making consistent use of the MIDlet class methods to satisfy the life cycle rules of MIDlets.

Viewing tip

I recommend that you open another copy of this document in a separate browser window and use the following links to easily find and view the figures and listings while you are reading about them.

Figures

  • Figure 1. Cell phone emulator output for MIDlet UI01 in active state.
  • Figure 2. Cell phone emulator output for MIDlet UI01 in paused state.
  • Figure 3. Partial standard output from MIDlet UI01.
  • Figure 4. Partial standard output from MIDlet SaveState01.

Listings

  • Listing 1. Beginning of the MIDlet named UI01.
  • Listing 2. The beginning of the startApp method for UI01.
  • Listing 3. The remainder of the startApp method for UI01.
  • Listing 4. The pauseApp method for UI01.
  • Listing 5. The destroyApp method for UI01.
  • Listing 6. The resume method for UI01.
  • Listing 7. Beginning of the Toggler class for UI01.
  • Listing 8. Beginning of the run method for the Toggler thread.
  • Listing 9. Loop for 2.5 cycles.
  • Listing 10. Signal the MIDlet to enter the active state.
  • Listing 11. Signal the MIDlet to enter the destroyed state.
  • Listing 12. Beginning of the Worker class for UI01.
  • Listing 13. Beginning of the run method for the Worker thread.
  • Listing 14. Worker thread enters an infinite loop.
  • Listing 15. Check the paused flag and behave accordingly.
  • Listing 16. The Worker thread goes to sleep.
  • Listing 17. Code executed when the kill flag is true.
  • Listing 18. Beginning of the class named SaveState01.
  • Listing 19. The pauseApp method for SaveState01.
  • Listing 20. The destroyApp method for SaveState01.
  • Listing 21. Beginning of the Worker class in SaveState01.
  • Listing 22. Beginning of the run method for the Worker class.
  • Listing 23. Loop and take naps while the user edits the TextBox.
  • Listing 24. Take a short nap.
  • Listing 25. The Worker thread dies.
  • Listing 26. The MIDlet program named UI01.
  • Listing 27. The MIDlet program named SaveState01.

Supplementary material

I recommend that you also study the other lessons in my extensive collection of online Java tutorials. You will find a consolidated index at www.DickBaldwin.com.

Discussion and sample code

 
MIDlet testing
The MIDlets in this lesson were tested using a Java SE 6 compiler, targeted at a V1.4 virtual machine, and WTK 2.5.2 running under Windows XP.

In the previous part of this lesson (see Resources), I showed you the code for two very simple MIDlets that were designed specifically to illustrate specific MIDlet programming concepts involving the user interface. Those MIDlets made very little effort to satisfy the rules for compliance with the life cycle requirements of a MIDlet.

In this second part of the lesson, I will present and explain two MIDlets that will show how to write user interface code while complying with the life cycle requirements of a MIDlet.

The second MIDlet will differ from the first. In addition to complying with the life cycle requirements, it will also attempt to minimize the memory footprint of the MIDlet while the MIDlet is in the paused state.

The MIDlet named UI01

The purpose of this MIDlet program is:

  • To illustrate a MIDlet user interface using a simple TextBox as an example.
  • To reinforce consistent use of the MIDlet class methods to satisfy the life cycle rules of MIDlets.
More threads are possible
There may be other active threads as well (such as the garbage collector thread) that are created by the virtual machine.

Three threads

This MIDlet consists of three threads: One thread is the main MIDlet thread that services the life-cycle methods.

The second thread is a Worker thread that services a TextBox object that can be edited by the user while the MIDlet is in the active state, but cannot be seen or edited while the MIDlet is in the paused state. The user can edit the contents of the TextBox across several active/paused MIDlet cycles without losing information in the process. This thread displays the contents of the TextBox each time the MIDlet enters the paused state, and displays the final contents of the TextBox when the MIDlet enters the destroyed state.

The third thread is a Toggler thread. It exists solely to force the MIDlet to toggle between the paused state and the active state in order to test and exercise the behavior of the Worker thread. After several cycles of toggling between the paused and active states, the Toggler thread causes the MIDlet to enter the destroyed state.

Will discuss in fragments

A complete listing of this program is presented in Listing 26. As is my custom, I will present and explain the MIDlet program in fragments. However, before getting into the details of the code, I will show you two screen shots of the output from the Sun cell phone emulator when the MIDlet is in the active state and the paused state along with a partial listing of the standard output from the MIDlet.

Cell phone emulator output for MIDlet UI01 in active state

Figure 1 shows the cell phone emulator output for the MIDlet when it was in the active state.  As you can see, I entered a portion of my name in the TextBox before the Toggler thread caused the MIDlet to enter the paused state.

Figure 1. Cell phone emulator output for MIDlet UI01 in active state.

Cell phone emulator output for MIDlet UI01 in paused state

Figure 2 shows the cell phone emulator output when the MIDlet was in the paused state. As you can see, this output completely obscured the output shown in Figure 1 because a different TextBox became the current Displayable object when the MIDlet entered the paused state.

Figure 2. Cell phone emulator output for MIDlet UI01 in paused state.

The fact that the text contents of the TextBox in Figure 2 are gray instead of black is a visual indication that the text is UNEDITABLE. The Incoming Call message on the screen was placed there by the cell phone emulator. Apparently this is the standard screen output for the Sun cell phone emulator when a MIDlet is in the paused state.

Original TextBox was restored

A very important aspect of the behavior of the MIDlet is that when the MIDlet once again became active, the display shown in Figure 1 returned to the screen. The text that was showing when the MIDlet entered the paused state was still there and available for editing.

Partial standard output from MIDlet UI01

Figure 3 shows a partial listing of the standard output produced by the MIDlet.

Figure 3. Partial standard output from MIDlet UI01.

OUT: MIDlet Constructed
OUT: MIDlet Started
OUT: Toggler constructed
OUT: Worker constructed
OUT: Toggler Thread Started
OUT: Worker Thread Started
OUT: MIDlet Paused
OUT: Worker awakened from a nap
OUT: TextBox contents: Dick B
OUT: MIDlet Re-Started
OUT: Worker awakened from the pause
OUT: MIDlet Paused
OUT: Worker awakened from a nap
OUT: TextBox contents: Dick Bald
OUT: MIDlet Re-Started
OUT: Worker awakened from the pause
OUT: MIDlet Destroyed
OUT: Toggler dying
OUT: Worker awakened from a nap
OUT: Final TextBox contents: Dick Baldwin
OUT: Worker committing suicide

Note the boldface text in particular

Much of the output in Figure 3 will become clear as we examine the code for the MIDlet and you see the print statements in the code. At this point, the three lines of text that I manually highlighted in boldface are the most important. The first two boldface lines of text show the contents of the TextBox each time the MIDlet entered the paused state. The third line of boldface text shows the final contents of the TextBox object. As you can see, the contents of the TextBox were not lost when the MIDlet entered the paused state and I was able to edit the TextBox by entering additional characters from my name each time the MIDlet entered the active state.

Two Displayable objects

Basically this MIDlet has two Displayable objects. One is shown in Figure 1 and the other is shown in Figure 2. When the MIDlet enters the active state, the object shown in Figure 1 becomes the current Displayable object. When the MIDlet enters the paused state, the object shown in Figure 2 becomes the current Displayable object. Therefore, the MIDlet is able to toggle between these two objects, making one or the other of them visible at any point in time with no loss of data.

Beginning of the MIDlet named UI01

Listing 1 shows the beginning of the MIDlet named UI01 including the constructor.

Listing 1. Beginning of the MIDlet named UI01.

public class UI01 extends MIDlet{

  //Variables needed only by the MIDlet.
  Worker theWorker;
  boolean running = false;

  //Variables needed by the Worker thread
  boolean paused;//true indicates paused
  boolean kill;//true means time for Worker to die

  //Variables needed by both Worker and Toggler threads
  UI01 theMIDlet;

  public UI01(){//Constructor
    System.out.println("MIDlet Constructed");
    theMIDlet = this;
    paused = true;
    kill = false;
  }//end constructor

The code in Listing 1 is straightforward and shouldn't require any explanation beyond the comments that are embedded in the code.

The beginning of the startApp method for UI01

The startApp method is called by the cell phone's Application Management Software (AMS) each time the state of the MIDlet changes from paused to active. The beginning of the startApp method is shown in Listing 2.

Listing 2. The beginning of the startApp method for UI01.

  public void startApp(){
    if(!running){
      //This is the first time that this method has been
      // called.
      System.out.println("MIDlet Started");
      running = true;
      //Create a new Toggler thread and start it running.
      new Toggler().start();

      //Create a new Worker thread and start it running
      // in an active state.
      paused = false;
      theWorker = new Worker();
      theWorker.start();

First transition from paused to active states

The MIDlet is initially in the paused state when it is launched by the user. The AMS calls the startApp method to cause the MIDlet to enter the active state. The code in Listing 2 is executed the first time the startApp method is called. (The code in Listing 3 is executed on each subsequent call to the startApp method.)

The first time the startApp method is called, the code in Listing 2 instantiates new objects of the Thread classes named Toggler and Worker and starts the two new threads running.

The remainder of the startApp method for UI01

The remainder of the startApp method is shown in Listing 3.

Listing 3. The remainder of the startApp method for UI01.

    }else{
      //This is not the first time that this method has
      // been called.
      System.out.println("MIDlet Re-Started");
      //Wake the Worker thread up if it is asleep. Set
      // the paused flag to false to tell the worker to
      // get to work.
      paused = false;
      theWorker.interrupt();
    }//end else
  }//end startApp

As mentioned earlier, the code in Listing 3 will be executed for each call to the startApp method following the first call to the method.

Wake the Worker thread

As you will see later, the Worker thread will have been put to sleep for a long time period when the MIDlet entered the paused state. The code in Listing 3 wakes it up by calling the interrupt method on the thread. The code in Listing 3 also sets the paused flag to false as a signal to the Worker thread that it should now be working and not sleeping because the MIDlet is in the active state. (Note, however, that even when the MIDlet is in the active state, the Worker thread takes a series of short naps to avoid consuming excessive computer resources.)

The pauseApp method for UI01

The pauseApp method is shown in its entirety in Listing 4. This method is called by theToggler thread to cause the MIDlet to enter the paused state. It may also be called by the AMS.

Listing 4. The pauseApp method for UI01.

  public void pauseApp(){
    //Tell Worker to go to sleep for a long time the next
    // time it checks the paused flag.
    paused = true;
    //Wake the worker up if it is asleep so that it will
    // check the pause flag quickly.
    theWorker.interrupt();
    System.out.println("MIDlet Paused");
    //Tell the AMS that this MIDlet is in the paused
    // state.
    notifyPaused();
  }//end pauseApp

Tell the worker to go to sleep for a long time period

The code in Listing 4 sets the paused flag to true as a signal to the Worker thread. The next time the Worker thread wakes up from a short nap and checks the status of the paused flag, it will go back to sleep for a very long time period. It is not intended that the Worker thread will awaken from this long period of sleep of its own accord. Rather, it is intended that it will sleep until the MIDlet once again becomes active, at which time it will be awakened by the startApp method.

Wake up from your nap and check the flag

Note that the code in Listing 4 calls the interrupt method on the Worker thread to wake it up if it is taking a nap. This will cause the thread to check the paused flag more quickly than if it were to continue napping until it awakens of its own accord.

The code in Listing 4 also calls the notifyPaused method to signal the AMS that the MIDlet has entered the paused state.

The destroyApp method for UI01

Listing 5 shows a complete listing of the destroyApp method. This method is called by the Toggler thread to cause the MIDlet to enter the destroyed state. It may also be called by the AMS. The incoming parameter is ignored in this implementation of the MIDlet.

Listing 5. The destroyApp method for UI01.

  public void destroyApp(boolean unconditional){
    //Tell the Worker to commit suicide the next time it
    // checks the kill flag.
    kill = true;
    //Wake the worker up if it is asleep so that it will
    // check the kill flag quickly.
    theWorker.interrupt();

    System.out.println("MIDlet Destroyed");
    //Tell the AMS that the MIDlet is in the destroyed
    // state. It can then be launched again.
    notifyDestroyed();
  }//end destroyApp

Time for the Worker thread to die

The code in Listing 5 sets the kill flag to signal the Worker thread that it should die the next time it wakes up from its nap and checks the kill flag. Then the code purposely wakes the thread up if it is napping. Finally, the code in Listing 5 calls the notifyDestroyed method to signal the AMS that it is entering the destroyed state. Presumably, at this point, the AMS will make the MIDlet object eligible for garbage collection following which the MIDlet can again be launched from the cell phone keypad.

The resume method for UI01

Listing 6 shows the resume method. This method is called by the Toggler thread to signal the MIDlet that it should request to be returned to the active state. If the request is honored by the AMS, it will call the startApp method.

Listing 6. The resume method for UI01.

  public void resume(){
    resumeRequest();
  }//end resume

Beginning of the Toggler class for UI01

The Beginning of the Toggler class for UI01 including the constructor is shown in Listing 7.

Listing 7. Beginning of the Toggler class for UI01.

  class Toggler extends Thread{

    Toggler(){
      System.out.println("Toggler constructed");
    }//end constructor

The class named Toggler is a member class. The purpose of an object of this class is to cause the MIDlet to toggle between the active state and the paused state for a total of 2.5 cycles (3 active periods and 2 paused periods). While the MIDlet is in the paused state, this thread displays uneditable text on the cell phone screen as shown in Figure 2. The AMS also displays an Incoming Call message during the time that the MIDlet is paused. After 2.5 cycles, this thread instructs the MIDlet to enter the destroyed state.

Beginning of the run method for the Toggler thread

Listing 8 shows the beginning of the run method for the Toggler thread.

Listing 8. Beginning of the run method for the Toggler thread.

    public void run(){
      System.out.println("Toggler Thread Started");
      
      //Create an UNEDITABLE TextBox
      TextBox textBox = new TextBox(
                                "Text Display by Toggler",
                                "",//text in TextBox
                                27,//width of TextBox
                                TextField.UNEDITABLE);

Recall that a thread's run method is executed when another thread calls the start method on the thread object, as was done in Listing 2.

Create an UNEDITABLE TextBox

Listing 8 uses the TextBox constructor to create a new TextBox object with an empty string and an UNEDITABLE constraint. (The TextBox will be populated later by calling the setString method on the TextBox object.)

Loop for 2.5 cycles

Listing 9 shows the beginning of a for loop that will loop for five iterations (2.5 full cycles with three active periods and two paused periods).

Listing 9. Loop for 2.5 cycles.

      for(int cnt = 0;cnt < 5;cnt++){
        if(cnt % 2 != 0){
          //Tell the thread to enter the paused
          // state on odd values of cnt.
          theMIDlet.pauseApp();

          //Now display some uneditable text while the
          // MIDlet is paused.
          //The TextBox must be wide enough to contain all
          // of the text.  Otherwise a
          // java/lang/IllegalArgumentException will be
          // thrown.
          textBox.setString(
                       "UNEDITABLE Toggler text: " + cnt);
          Display.getDisplay(theMIDlet).
                                      setCurrent(textBox);

          //Sleep for two seconds with the MIDlet in the
          // paused state. Then go back to the top of the
          // loop.
          try{Thread.currentThread().sleep(2000);
          }catch(Exception e){}

Signal the MIDlet to enter the paused state

This thread uses the odd versus even value of the for loop counter to decide whether to signal the MIDlet to pause or to become active. When the value of cnt is odd, the code in Listing 9 calls the pauseApp method on the MIDlet thread to signal the MIDlet that it should enter the paused state (see Listing 4).

Populate and display the TextBox and go to sleep

Then the code in Listing 9 calls the setString method to populate the TextBox with a string that includes the current value of cnt. Then it calls the setCurrent method to cause the TextBox to become the visible Displayable object. Following that, the Toggler thread sleeps for two seconds and then returns control to the top of the for loop.

Signal the MIDlet to enter the active state

Listing 10 shows the code that is executed when the value of the loop counter is even.

Listing 10. Signal the MIDlet to enter the active state.

        }else{//The value of cnt is even.
          theMIDlet.resume();

          //Sleep for four seconds with the MIDlet in the
          // active state.  Then go back to the top of the
          // loop.
          try{Thread.currentThread().sleep(4000);
          }catch(Exception e){}
        }//end else
      }//end for loop

This code calls the resume method on the MIDlet, which causes the MIDlet to send a request to the AMS to return it to the active state. If the request is honored, the AMS will call the startApp method on the MIDlet thread. Note in particular that this code does not call the startApp method directly but delegates that action to the AMS by way of the MIDlet.

Signal the MIDlet to enter the destroyed state

Listing 11 shows the code that is executed when the for loop that began in Listing 9 terminates.

Listing 11. Signal the MIDlet to enter the destroyed state.

      theMIDlet.destroyApp(true);

      //This thread will die at this point.
      System.out.println("Toggler dying");
    }//end run
  }//end class Toggler

Listing 11 calls the destroyApp method (see Listing 5) to signal the MIDlet that it should enter the destroyed state. Then the Toggler thread's run method terminates, which causes the Toggler thread to die a natural death.

Beginning of the Worker class for UI01

Listing 12 shows the beginning of the Worker class including the constructor.

Listing 12. Beginning of the Worker class for UI01.

  class Worker extends Thread{
    long nap = 1;//Sleep for one millisecond
    long longSleep = 25000000;//6.94 hours
    long sleepTime = nap;
    String title = "Enter Text";//TextBox title
    int width = 50;//TextBox width.

    Worker(){
      System.out.println("Worker constructed");
    }//end constructor

The purpose of an object of this member class is to do some work while the MIDlet is in the active state and to sleep while the MIDlet is in the paused state. An object of this class also sleeps some of the time while the MIDlet is in the active state to avoid consuming major computational resources.

The general behavior of this thread is:

  • Create an empty TextBox when the thread is started.
  • Set the title for the TextBox.
  • Allow the user to enter text in the TextBox while the MIDlet is in the active state.
  • Display the current contents of the TextBox when the MIDlet enters the paused state.
  • Display the final contents of the TextBox when the MIDlet is destroyed.

Beginning of the run method for the Worker thread

The run method for the Worker thread begins in Listing 13. (Recall that the start method is called on this thread by the startApp method in Listing 2.)

Listing 13. Beginning of the run method for the Worker thread.

    public void run(){
      System.out.println("Worker Thread Started");
      //Create and display an empty TextBox.
      TextBox textBox = new TextBox(title,
                                    "",//TextBox text
                                    width,
                                    TextField.ANY);
      Display.getDisplay(theMIDlet).setCurrent(textBox);

Listing 13 creates and displays a new empty TextBox object that will accept any input characters from the user.

Worker thread enters an infinite loop

The worker thread enters an infinite loop in Listing 14.

Listing 14. Worker thread enters an infinite loop.

      while(true){//Enter an infinite loop
        if(kill){//Check the kill flag
          //The kill flag is true.
          //This break will cause the thread to break out
          // of the while loop and die.
          break;
        }//end if

Immediately upon entering the loop, Listing 14 checks to see if the kill flag has been set to true by the MIDlet's destroyApp method (see Listing 5). If so, Listing 14 executes a break statement, which causes the while loop to terminate.  You will see later that the Worker thread dies upon termination of the loop.

Check the paused flag and behave accordingly

At this point the Worker thread checks the value of the paused flag, which is set to true by the pauseApp method (see Listing 4) and set to false by the startApp method (see Listing 2 and Listing 3). Depending on the value of the paused flag, the Worker thread will either:

  • Take a short nap to avoid consuming computer resources if the flag is false, or
  • Display the contents of the TextBox and go to sleep for a very long time period if the flag is true.

The code in Listing 15 performs the check on the paused flag and sets the value of a variable named sleepTime to represent either a short nap or a very long sleep.

Listing 15. Check the paused flag and behave accordingly.

        if(!paused){//Check the paused flag.
          //The MIDlet is not in the paused state. Sleep
          // for a short period of time and allow the
          // user to edit the text in the TextBox. Then
          // loop back around and check the paused flag
          // again.
          sleepTime = nap;
        }else{
          //The MIDlet is in the paused state. Display the
          // text in the TextBox.  Then go to sleep for a
          // very long time.  Will wake when the startApp
          // method calls the interrupt method on the
          // thread.
          System.out.println("TextBox contents: "
                                   + textBox.getString());
          sleepTime = longSleep;
        }//end else

The Worker thread goes to sleep

In Listing 16, the Worker thread goes to sleep for a period of time determined by the value of the variable named sleepTime.

Listing 16. The Worker thread goes to sleep.

        try{
          Thread.currentThread().sleep(sleepTime);
        }catch(InterruptedException e){
          if(sleepTime == longSleep){
            System.out.println(
                     "Worker awakened from the pause");
          }else{
            System.out.println(
                     "Worker awakened from a nap");
          }//end else

          Display.getDisplay(theMIDlet).setCurrent(
                                                 textBox);
        }//end catch

      }//end while loop

If the worker thread wakes of its own accord:

  • The code in the catch lock in Listing 16 will be skipped.
  • Control will return to the top of the while loop in Listing 14.
  • The kill and paused flags will be hecked again.
  • The entire process will be repeated.

This is likely to happen when the MIDlet is in the active state and the Worker thread is taking a series of short naps.

If the interrupt method is called on the Worker thread while it is sleeping, the code in the catch block will be executed. This can happen as the result of code executed in the life-cycle methods in the main MIDlet thread (see Listings 3, 4, and 5).

When the code in the catch block is executed:

  • The Worker thread announces that it has been awakened.
  • The TextBox belonging to the Worker thread is made the current Displayable object (see the explanation below).
  • Control returns to the top of the while loop and the process is repeated.

Making the Worker thread's TextBox the current Displayable object

Recall that in Listing 9, the Toggler thread's TextBox was made the current Displayable object.  This caused it to be visible on the cell phone screen during the entire period that the MIDlet was paused as shown in Figure 2.

There are only three cases in which the catch block in Listing 16 will be executed:

  1. The MIDlet is entering the paused state (see Listing 4).
  2. The MIDlet is entering the destroyed state (see Listing 5).
  3. The MIDlet is entering the active state from the paused state (see Listing 3).

The third circumstance is the important one

For the first two cases listed above, setting the Worker thread's TextBox to the current Displayable object is of no consequence. However, this code is necessary for the third case where the MIDlet is transitioning from the paused state to the active state.

During the paused state, the screen display is as shown in Figure 2. It is necessary to set the Worker thread's TextBox to the current Displayable object to return the display to that shown in Figure 1.

The end of the while loop

Listing 16 signals the end of the while loop. When the code in the catch block has executed, control will return to the top of the while loop where the kill flag will be tested for true. Recall that the value of the kill flag is set to true by the MIDlet's destroyApp method (see Listing 5).

Code executed when the kill flag is true

The code in Listing 17 is executed when the code in Listing 14 finds the kill flag to be true and executes a break statement to break out of the loop.

Listing 17. Code executed when the kill flag is true.

      System.out.println("Final TextBox contents: "
                                   + textBox.getString());
      System.out.println("Worker committing suicide");
    }//end run
  }//end class Worker
  //====================================================//

}//end class UI01

Listing 17 displays the final contents of the Worker thread's TextBox.  Then the run method terminates causing the Worker thread to die a natural death.

Listing 17 also signals the end of the Worker class and the end of the MIDlet program named UI01.

The MIDlet named SaveState01

The purpose of this MIDlet is to:

  • Introduce the topic of the MIDlet user interface using a simple TextBox as an example.
  • Illustrate one approach to minimizing the MIDlet's memory footprint while the MIDlet is paused.
  • Reinforce consistent use of the MIDlet class methods to satisfy the life cycle rules of MIDlets.

Three threads

As in the MIDlet named UI01, this MIDlet consists of three threads. One thread is the main MIDlet thread that services the life-cycle methods.

The second thread

The second thread is a Worker thread. It services a TextBox object that can be edited by the user while the MIDlet is active. When the MIDlet enters the paused state, the contents of the TextBox object are saved, and the Worker thread object, including the TextBox is made eligible for garbage collection.

When the MIDlet re-enters the active state, a new Worker thread, including a new TextBox is created. The new TextBox is populated with the contents that were saved from the previous TextBox. Therefore, the user can edit the contents of the TextBox across several active/paused MIDlet cycles without losing information in the process.

The third thread

The third thread is a Toggler thread, which forces the MIDlet to toggle between the paused state and the active state several times in order to test and exercise the ability of the Worker thread to release memory when the MIDlet enters the paused state, and to restore the state of the Worker thread and its TextBox when the MIDlet returns to the active state.

After several cycles of toggling the MIDlet between the paused and active states, the Toggler thread causes the MIDlet to enter the destroyed state.

The external behavior of the MIDlet

The external behavior of this MIDlet is very similar to the earlier MIDlet named UI01. However, this MIDlet differs from the earlier MIDlet in one very significant way: it relinquishes the memory occupied by the Worker thread each time the MIDlet enters the paused state. When the MIDlet re-enters the active state, a new Worker thread is constructed using the information that was saved earlier.

Standard output from the MIDlet named SaveState01

There is very little difference between the cell phone emulator output produced by this MIDlet and the output shown in Figure 1 and Figure 2. Therefore, I won't show you screen shots from the cell phone emulator for this MIDlet. However, there are some differences in the standard output produced by this MIDlet and the earlier MIDlet, so I will show you the standard output, which is contained in Figure 4.

Figure 4. Partial standard output from MIDlet SaveState01.

OUT: MIDlet Constructed
OUT: MIDlet Started
OUT: Toggler constructed
OUT: Worker constructed
OUT: Toggler Thread Started
OUT: Worker Thread Started
OUT: Worker awakened from a nap
OUT: TextBox contents: Dick B
OUT: Worker dead, eligible for garbage collection.
OUT: MIDlet Paused
OUT: MIDlet Re-Started
OUT: Worker constructed
OUT: Worker Thread Started
OUT: Worker awakened from a nap
OUT: TextBox contents: Dick Bald
OUT: Worker dead, eligible for garbage collection.
OUT: MIDlet Paused
OUT: MIDlet Re-Started
OUT: Worker constructed
OUT: Worker Thread Started
OUT: Worker awakened from a nap
OUT: TextBox contents: Dick Baldwin
OUT: Worker dead, eligible for garbage collection.
OUT: Final TextBox contents: Dick Baldwin
OUT: MIDlet Destroyed

Note the boldface text

As before, the most important part of the standard output is the four lines of text that I manually highlighted in boldface. These four lines of text show the contents of the TextBox at four different points in time as the MIDlet cycles between the active and paused states. Each time the MIDlet transitioned from the paused state to the active state, the previous contents of the TextBox are restored and made available for editing by the user.

Will discuss in fragments

As usual, I will present and explain this MIDlet in fragments. A complete listing of this MIDlet is shown in Listing 27. Much of the code in this MIDlet is similar to or identical to the code that I explained earlier, and I won't repeat that explanation here. Rather, I will concentrate on explaining the differences between this MIDlet and the previous MIDlet.

Beginning of the class named SaveState01

The beginning of the class named SaveState01, including the entire method named startApp is shown in Listing 18. For brevity, I deleted some of the code from Listing 18 that is identical to the code in the previous MIDlet. I also highlighted new code in this MIDlet in boldface.

Listing 18. Beginning of the class named SaveState01.

public class SaveState01 extends MIDlet{

  //Code deleted for brevity

  //Variables needed by the Worker thread
  boolean paused;//true indicates paused
  boolean kill;//true means time for the Worker to die
  String savedText = "";//TextBox text is saved here.

  //Code deleted for brevity

  //The following method is called by the AMS to change
  // the state of the MIDlet from paused to active.
  public void startApp(){
    if(!midletRunning){
      //Code deleted for brevity
      //Unlike the previous MIDlet, no Worker thread is
      // created here.
      
    }else{
      //Code deleted for brevity.
      //Unlike the previous MIDlet, there is no requirement
      // to wake a sleeping Worker thread here.
    }//end else

    //Create a new Worker thread and start it running in
    // an active MIDlet state. Note that this code follows
    // the if-else statement.
    paused = false;
    theWorker = new Worker();
    theWorker.start();
  }//end startApp

A new instance variable

The class begins by declaring a new instance variable named savedText. It is used by the Worker thread to save the current contents of the TextBox before the thread dies and becomes eligible for garbage collection. Note that this variable is initialized to an empty string in Listing 18.

Construct a new Worker thread

When the MIDlet enters the paused state, the Worker thread dies and becomes eligible for garbage collection. Therefore, there is no need for the code in the startApp method to wake a sleeping Worker thread. Rather, a new Worker thread must be constructed and populated each time the startApp method is called as shown by the boldface code at the end of the method.

Note however that the Worker thread still takes a series of short naps while the MIDlet is active to reduce the consumption of computational resources. Therefore, it is still necessary for the pauseApp method and the destroyApp method to wake the Worker thread from its nap to cause it to check the flags more quickly.

The pauseApp method for SaveState01

The pauseApp method is shown in Listing 19. This method is called by the Toggler thread to cause the MIDlet to enter the paused state. It may also be called by the AMS.

Listing 19. The pauseApp method for SaveState01.

  public void pauseApp(){
    //Tell Worker to die the next time it checks the
    // paused flag.
    paused = true;
    //Wake the worker up if it is asleep so that it will
    // check the paused flag quickly.
    theWorker.interrupt();
    //Delay until the Worker thread dies.
    while(theWorker.isAlive()){}
    //Make the worker object eligible for garbage
    // collection.
    theWorker = null;
    System.out.println(
         "Worker dead, eligible for garbage collection.");
    System.out.println("MIDlet Paused");

    //Tell the AMS that this MIDlet is in the paused
    // state.
    notifyPaused();
  }//end pauseApp

Wait for the Worker thread to die

The things that are different about this version of the method are highlighted in boldface.

To begin with, in this MIDlet, the Worker is expected to die rather than to simply go to sleep for a long time when it finds that the paused flag has been set to true.

That being the case, the code in Listing 19 delays until it can confirm that the Worker thread is dead. Then it sets the reference to the Worker object to null making the object eligible for garbage collection.

Having accomplished that, it prints a message and notifies the AMS that the MIDlet is entering the paused state.

The destroyApp method for SaveState01

The destroyApp method is shown in Listing 20. This method is called by the Toggler thread to cause the MIDlet to enter the destroyed state. It may also be called by the AMS. (The incoming parameter is ignored in this MIDlet.)

Listing 20. The destroyApp method for SaveState01.

  public void destroyApp(boolean unconditional){
    //Tell the Worker to die the next time it checks the
    // kill flag.
    kill = true;
    //Wake the worker up if it is asleep so that it will
    // check the kill flag quickly.
    theWorker.interrupt();

    //Delay until the Worker thread dies to make certain
    // that all of the text entered by the user is stored
    // in savedText.
    while(theWorker.isAlive()){}
    //Make the worker object eligible for garbage
    // collection.
    theWorker = null;
    System.out.println(
         "Worker dead, eligible for garbage collection.");

    System.out.println(
                  "Final TextBox contents: " + savedText);

    System.out.println("MIDlet Destroyed");

    //It is assumed that the following method call will
    // cause the AMS to make the MIDlet object eligible
    // for garbage collection, which in turn will make the
    // Toggler object eligible for garbage collection.

    //Tell the AMS that the MIDlet is in the destroyed
    // state. It can then be launched again.
    notifyDestroyed();
  }//end destroyApp

The rationale for the new code in the destroyApp method is essentially the same as the rationale for the new code in the pauseApp method, so it shouldn't be necessary for me to explain that rationale again. Note also that the method named resume is identical and serves the same purpose in the two MIDlets, so I won't include another fragment showing the resume method.

The Toggler thread

There is essentially no difference between the Toggler class in the previous MIDlet named UI01 and the Toggler class in this MIDlet. Therefore, another discussion of the Toggler thread should not be necessary.

The Worker class

As before, the purpose of an object of the member class named Worker is to do some work while the MIDlet is in the active state. However, unlike the previous MIDlet, the purpose of a Worker object in this MIDlet is to become eligible for garbage collection when the MIDlet enters the paused state.

An object of the Worker class also takes short naps while the MIDlet is in the active state to avoid consuming excessive computational resources.

Behavior of the Worker thread

The behavior of the Worker thread is:

  • Create an empty TextBox when the thread is started.
  • Set the title for the TextBox.
  • Allow the user to enter text into the TextBox while the MIDlet is in the active state.
  • Save the contents of the TextBox when the MIDlet enters the paused state.
  • Display the current contents of the TextBox when the MIDlet enters the paused state.
  • Make the Worker object eligible for garbage collection when the MIDlet enters the paused state.
  • Create a new Worker thread including a TextBox object when the MIDlet enters the active state. Go back to the top.

The code in the Worker class in this MIDlet is considerably different from the code in the same class in the MIDlet named UI01. Therefore, rather than to compare the two, I will simply start from scratch and explain the code in this class.

Beginning of the Worker class in SaveState01

The Worker class, including the constructor begins in Listing 21.

Listing 21. Beginning of the Worker class in SaveState01.

  class Worker extends Thread{
    long nap = 1;//Sleep for one millisecond
    int width = 50;//TextBox width.
    String title = "Enter Text";//TextBox title
    TextBox textBox;

    Worker(){
      System.out.println("Worker constructed");
    }//end constructor

The code in Listing 21 is straightforward and shouldn't require an explanation.

Beginning of the run method for the Worker class

The run method begins in Listing 22.

Listing 22. Beginning of the run method for the Worker class.

    public void run(){
      System.out.println("Worker Thread Started");

      textBox = new TextBox(title,
                            savedText,
                            width,
                            TextField.ANY);
      //Display the new TextBox so the user can edit
      // its contents.
      Display.getDisplay(theMIDlet).setCurrent(textBox);

After announcing that the thread has been started, Listing 22 creates a new TextBox and populates it with the text that was saved when the MIDlet entered the paused state earlier. That variable was initialized to an empty string when the MIDlet was launched so the first time the TextBox is created it is empty.

Loop and take naps while the user edits the TextBox

The Worker thread enters an infinite loop in Listing 23.

Listing 23. Loop and take naps while the user edits the TextBox.

      while(true){
        if(kill || paused){//Check the flags
          savedText = textBox.getString();
          System.out.println(
                        "TextBox contents: " + savedText);
          break;

The thread loops and takes short naps to avoid consuming excessive computer resources while the user edits the TextBox. It checks the value of the kill and paused flags at the beginning of each iteration of the loop. Recall that the paused flag is set to true by the pauseApp method and the kill flag is set to true by the destroyApp method. (The same flag could have been used for both, but they were separated to make it easier to follow the rationale for the code.)

If either flag is found to be true, the Worker thread:

  • Saves the contents of the TextBox.
  • Displays the contents of the TextBox.
  • Breaks out of the loop and dies (see Listing 25).

Take a short nap

If neither the kill flag nor the paused flag is set, the Worker thread executes the code in Listing 24.

Listing 24. Take a short nap.

        }else{
          try{
            Thread.currentThread().sleep(nap);
          }catch(InterruptedException e){
            System.out.println(
                            "Worker awakened from a nap");
          }//end catch
        }//end else

      }//end while loop

If control reaches the else clause in Listing 24, the MIDlet is not in the paused state and is not being destroyed. The Worker thread sleeps for a short period of time to allow the user to edit the text in the TextBox. Then control goes back to the top of the while loop in Listing 23 where the values of the kill and pause flags are checked again.

The Worker thread dies

This process continues until one or the other of the flags is found to be true, at which time control breaks out of the while loop and goes to Listing 25.

Listing 25. The Worker thread dies.

    }//end run
  }//end class Worker
  //====================================================//

}//end class SaveState01

When control reaches Listing 25, the run method terminates and the Worker thread dies.

Doesn't know and, doesn't care...

The Worker thread doesn't know and doesn't care whether the MIDlet is entering the paused state or the destroyed state. In either case, the thread's responsibility is to die so that the reference to the Worker object can be set to null making the Worker object eligible for garbage collection. Because the contents of the TextBox were saved in Listing 23, a new Worker thread can be instantiated and its TextBox can be populated with the saved string later if needed.

Listing 25 also signals the end of the Worker class and the end of the MIDlet program class.

Run the programs

I encourage you to copy the code from Listing 26 and Listing 27 and run it in the MIDlet development framework that I provided in the earlier lesson titled Capturing Output Produced by Programs Running in a Child Process (see Resources).

Experiment with the MIDlet code, making changes and observing the effects of your changes. See if you can explain the effects of your changes.

Don't forget that you will need to download and install the latest version of the Sun Java Wireless Toolkit for CLDC (see Resources). As of the date this lesson is being written, the latest version of the toolkit is WTK2.5.2.

Summary

In this part of this lesson, I

  • Showed you a MIDlet user interface using a simple TextBox as an example.
  • Showed you one approach for minimizing the MIDlet's memory footprint while the MIDlet is paused.
  • Reinforced consistent use of the MIDlet class methods to satisfy the life cycle rules of MIDlets.

What's next?

In the next lesson you will learn

  • How to program an Alert for a MIDlet.
  • How to add an image to the Alert.
  • How to add a Gauge to the Alert to act as a progress or activity meter.
  • How to use a Timer to control when an Alert becomes visible on the screen.

Resources

Complete program listings

Complete listings of the programs discussed in this lesson are provided in Listing 26 and Listing 27 below.

Listing 26. The MIDlet program named UI01.

/*UI01.java
Copyright 2007, R.G.Baldwin
December 13, 2007

The purpose of this MIDlet program is to:

1. Introduce the topic of the MIDlet user interface using
a simple TextBox as an example.

2. Reinforce consistent use of the MIDlet class methods
to satisfy the life cycle rules of MIDlets.

Three threads were purposely constructed and started in
this MIDlet.  The three threads are:

1. The main MIDlet thread that services the life-cycle
methods.

2. A Worker thread that services a TextBox object that can
be edited by the user while the MIDlet is in the active
state, but cannot be seen or edited while the MIDlet is in
the paused state. The user can edit the contents of the
TextBox across several active/paused MIDlet cycles without
losing information in the process. This thread displays
the contents of the TextBox each time the MIDlet enters
the paused state, and displays the final contents of the
TextBox when the MIDlet enters the destroyed state.

3. A Toggler thread. It exists solely to force the MIDlet
to toggle between the paused state and the active state
in order to test and exercise the behavior of the Worker
thread. After several cycles of toggling between the
paused and active states, the Toggler thread causes the
MIDlet to be destroyed.

Tested using a Java SE 6 compiler, targeted at a V1.4
virtual machine, and WTK 2.5.2 running under Windows XP.
*********************************************************/

package UI01;

import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.TextBox;
import javax.microedition.lcdui.TextField;
import javax.microedition.midlet.MIDlet;
import java.lang.Thread;

public class UI01 extends MIDlet{

  //Variables needed only by the MIDlet.
  Worker theWorker;
  boolean running = false;

  //Variables needed by the Worker thread
  boolean paused;//true indicates paused
  boolean kill;//true means time for Worker to die

  //Variables needed by both Worker and Toggler threads
  UI01 theMIDlet;

  public UI01(){//Constructor
    System.out.println("MIDlet Constructed");
    theMIDlet = this;
    paused = true;
    kill = false;
  }//end constructor

  //The following method is called by the AMS to change
  // the state of the MIDlet from paused to active.
  public void startApp(){
    if(!running){
      //This is the first time that this method has been
      // called.
      System.out.println("MIDlet Started");
      running = true;
      //Create a new Toggler thread and start it running.
      new Toggler().start();

      //Create a new Worker thread and start it running
      // in an active state.
      paused = false;
      theWorker = new Worker();
      theWorker.start();

    }else{
      //This is not the first time that this method has
      // been called.
      System.out.println("MIDlet Re-Started");
      //Wake the Worker thread up if it is asleep. Set
      // the paused flag to false to tell the worker to
      // get to work.
      paused = false;
      theWorker.interrupt();
    }//end else
  }//end startApp
  //----------------------------------------------------//

  //This method is called by the Toggler thread to cause
  // the MIDlet to enter the paused state. It may also be
  // called by the AMS.
  public void pauseApp(){
    //Tell Worker to go to sleep for a long time the next
    // time it checks the paused flag.
    paused = true;
    //Wake the worker up if it is asleep so that it will
    // check the pause flag quickly.
    theWorker.interrupt();
    System.out.println("MIDlet Paused");
    //Tell the AMS that this MIDlet is in the paused
    // state.
    notifyPaused();
  }//end pauseApp
  //----------------------------------------------------//

  //This method is called by the Toggler thread to cause
  // the MIDlet to enter the destroyed state. It may also
  // be called by the AMS. The incoming parameter is
  // ignored in this MIDlet.
  public void destroyApp(boolean unconditional){
    //Tell the Worker to commit suicide the next time it
    // checks the kill flag.
    kill = true;
    //Wake the worker up if it is asleep so that it will
    // check the kill flag quickly.
    theWorker.interrupt();

    System.out.println("MIDlet Destroyed");
    //Tell the AMS that the MIDlet is in the destroyed
    // state. It can then be launched again.
    notifyDestroyed();
  }//end destroyApp
  //----------------------------------------------------//

  //This method is called by the Toggler thread to
  // instruct the MIDlet to request that it be returned
  // to the active state.
  public void resume(){
    resumeRequest();
  }//end resume
  //====================================================//

  //This is a member class.  The purpose of an object of
  // this class is to cause the MIDlet to toggle between
  // the active state and the paused state for a total
  // of 2.5 cycles (3 active periods and 2 paused
  // periods). While the MIDlet is in the paused state,
  // this thread displays uneditable text on the screen.
  // The AMS also displays an Incoming Call message during
  // the time that the MIDlet is paused. After 2.5 cycles,
  // this thread instructs the MIDlet to enter the
  // destroyed state.
  class Toggler extends Thread{

    Toggler(){
      System.out.println("Toggler constructed");
    }//end constructor

    public void run(){
      System.out.println("Toggler Thread Started");
      
      //Create an UNEDITABLE TextBox
      TextBox textBox = new TextBox(
                                "Text Display by Toggler",
                                "",//text in TextBox
                                27,//width of TextBox
                                TextField.UNEDITABLE);
      //Loop for 2.5 cycles
      for(int cnt = 0;cnt < 5;cnt++){
        if(cnt % 2 != 0){
          //Tell the thread to enter the paused
          // state on odd values of cnt.
          theMIDlet.pauseApp();

          //Now display some uneditable text while the
          // MIDlet is paused.
          //The TextBox must be wide enough to contain all
          // of the text.  Otherwise a
          // java/lang/IllegalArgumentException will be
          // thrown.
          textBox.setString(
                       "UNEDITABLE Toggler text: " + cnt);
          Display.getDisplay(theMIDlet).
                                      setCurrent(textBox);

          //Sleep for two seconds with the MIDlet in the
          // paused state. Then go back to the top of the
          // loop.
          try{Thread.currentThread().sleep(2000);
          }catch(Exception e){}

        }else{//The value of cnt is even.
          //Cause the thread to send a request to
          // the AMS to return it to the active
          // state.  If the request is honored, the
          // AMS will call the startApp method on the
          // thread. Note in particular that this
          // code does not call the startApp method
          // directly but delegates that action to the
          // AMS by way of the MIDlet.
          theMIDlet.resume();

          //Sleep for four seconds with the MIDlet in the
          // active state.  Then go back to the top of the
          // loop.
          try{Thread.currentThread().sleep(4000);
          }catch(Exception e){}
        }//end else
      }//end for loop

      //Instruct the thread to enter the destroyed state
      // when control exits the loop and reaches this
      // point.
      theMIDlet.destroyApp(true);

      //This thread will die at this point.
      System.out.println("Toggler dying");
    }//end run
  }//end class Toggler
  //====================================================//

  //This is a member class.  The purpose of an object of
  // this class is to do some work while the MIDlet is in
  // the active state and to sleep while the MIDlet is in
  // the paused state.  It also sleeps some while the
  // MIDlet is in the active state to avoid consuming
  // major computational resources.  The work done by the
  // thread is:
  //  - Create an empty TextBox when the thread is
  //    started.
  //  - Set the title for the TextBox.
  //  - Allow the user to enter text in the TextBox while
  //    the MIDlet is in the active state.
  //  - Display the current contents of the TextBox when
  //    the MIDlet enters the paused state.
  //  - Display the final contents of the TextBox when the
  //    MIDlet is destroyed.
  class Worker extends Thread{
    long nap = 1;//Sleep for one millisecond
    long longSleep = 25000000;//6.94 hours
    long sleepTime = nap;
    String title = "Enter Text";//TextBox title
    int width = 50;//TextBox width.

    Worker(){
      System.out.println("Worker constructed");
    }//end constructor

    public void run(){
      System.out.println("Worker Thread Started");
      //Create and display an empty TextBox.
      TextBox textBox = new TextBox(title,
                                    "",//TextBox text
                                    width,
                                    TextField.ANY);
      Display.getDisplay(theMIDlet).setCurrent(textBox);

      while(true){//Enter an infinite loop
        if(kill){//Check the kill flag
          //The kill flag is true.
          //This break will cause the thread to break out
          // of the while loop and die.
          break;
        }//end if

        if(!paused){//Check the paused flag.
          //The MIDlet is not in the paused state. Sleep
          // for a short period of time and allow the
          // user to edit the text in the TextBox. Then
          // loop back around and check the paused flag
          // again.
          sleepTime = nap;
        }else{
          //The MIDlet is in the paused state. Display the
          // text in the TextBox.  Then go to sleep for a
          // very long time.  Will awake when the startApp
          // method calls the interrupt method on the
          // thread.
          System.out.println("TextBox contents: "
                                   + textBox.getString());
          sleepTime = longSleep;
        }//end else

        try{
          Thread.currentThread().sleep(sleepTime);
        }catch(InterruptedException e){
          //Control reaches here when the startApp method
          // calls the interrupt() method on this thread
          // while it is sleeping. Loop back to the top
          // and either terminate or do some work
          // depending on the state of the flags. If the
          // thread wakes up on its own accord, this code
          // will not be executed.
          if(sleepTime == longSleep){
            System.out.println(
                     "Worker awakened from the pause");
          }else{
            System.out.println(
                     "Worker awakened from a nap");
          }//end else

          //Display the TextBox so the user can edit
          // its contents.  This is necessary because the
          // Toggler thread displayed a different Screen
          // while the MIDlet was paused.
          Display.getDisplay(theMIDlet).setCurrent(
                                                 textBox);
        }//end catch

      }//end while loop

      //Control is transferred to here when the kill flag
      // is found to be true and a break is executed
      // inside the while loop.

      //This thread will die at this point.  Before dying,
      // display the final contents of the TextBox.
      System.out.println("Final TextBox contents: "
                                   + textBox.getString());
      System.out.println("Worker committing suicide");
    }//end run
  }//end class Worker
  //====================================================//

}//end class UI01
//======================================================//

 

Listing 27. The MIDlet program named SaveState01.

/*SaveState01.java
Copyright 2007, R.G.Baldwin
December 13, 2007

The purpose of this MIDlet program is to:

1. Introduce the topic of the MIDlet user interface using
a simple TextBox as an example.

2. Illustrate one approach to minimizing the MIDlet's 
memory footprint while the MIDlet is paused.

3. Reinforce consistent use of the MIDlet class methods
to satisfy the life cycle rules of MIDlets.

Three threads were purposely constructed and started in
this MIDlet:

1. The main MIDlet thread that services the life-cycle
methods.

2. A Worker thread. It services a TextBox object that can
be edited by the user while the MIDlet is active. When the
MIDlet enters the paused state, the contents of the 
TextBox object are saved, and the Worker thread is made 
eligible for garbage collection. When the MIDlet re-enters
the active state, a new Worker is created and populated 
with the contents that were saved from the previous 
TextBox.  Therefore, the user can edit the contents of the
TextBox across several active/paused MIDlet cycles without 
losing information in the process.

3. A Toggler thread, which forces the MIDlet
to toggle between the paused state and the active state
in order to test and exercise the ability of the Worker
thread to release memory resources when the MIDlet
enters the paused state, and to restore the state of
the TextBox when the MIDlet returns to the active state.
After several cycles of toggling between the paused and
active states, the Toggler thread causes the MIDlet to
enter the destroyed state.

Tested using a Java SE 6 compiler, targeted at a V1.4
virtual machine, and WTK 2.5.2 running under Windows XP.
*********************************************************/

package SaveState01;

import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.TextBox;
import javax.microedition.lcdui.TextField;
import javax.microedition.midlet.MIDlet;
import java.lang.Thread;

public class SaveState01 extends MIDlet{

  //Variables needed only by the MIDlet.
  Worker theWorker;
  boolean midletRunning = false;

  //Variables needed by the Worker thread
  boolean paused;//true indicates paused
  boolean kill;//true means time for the Worker to die
  String savedText = "";//TextBox text is saved here.

  //Variables needed by both Worker and Toggler threads
  SaveState01 theMIDlet;

  public SaveState01(){//Constructor
    System.out.println("MIDlet Constructed");
    theMIDlet = this;
    paused = true;
    kill = false;
  }//end constructor

  //The following method is called by the AMS to change
  // the state of the MIDlet from paused to active.
  public void startApp(){
    if(!midletRunning){
      //This is the first time that this method has been
      // called.
      System.out.println("MIDlet Started");
      midletRunning = true;

      //Create a new Toggler object and start it running.
      new Toggler().start();
      
    }else{
      //This is not the first time that this method has
      // been called.
      System.out.println("MIDlet Re-Started");
    }//end else

    //Create a new Worker thread and start it running in
    // an active MIDlet state.
    paused = false;
    theWorker = new Worker();
    theWorker.start();
  }//end startApp
  //----------------------------------------------------//

  //This method is called by the Toggler thread to cause
  // the MIDlet to enter the paused state. It may also be
  // called by the AMS.
  public void pauseApp(){
    //Tell Worker to die the next time it checks the
    // paused flag.
    paused = true;
    //Wake the worker up if it is asleep so that it will
    // check the paused flag quickly.
    theWorker.interrupt();
    //Delay until the Worker thread dies.
    while(theWorker.isAlive()){}
    //Make the worker object eligible for garbage
    // collection.
    theWorker = null;
    System.out.println(
         "Worker dead, eligible for garbage collection.");
    System.out.println("MIDlet Paused");

    //Tell the AMS that this MIDlet is in the paused
    // state.
    notifyPaused();
  }//end pauseApp
  //----------------------------------------------------//

  //This method is called by the Toggler thread to cause
  // the MIDlet to enter the destroyed state. It may also
  // be called by the AMS. The incoming parameter is
  // ignored in this MIDlet.
  public void destroyApp(boolean unconditional){
    //Tell the Worker to die the next time it checks the
    // kill flag.
    kill = true;
    //Wake the worker up if it is asleep so that it will
    // check the kill flag quickly.
    theWorker.interrupt();

    //Delay until the Worker thread dies to make certain
    // that all of the text entered by the user is stored
    // in savedText.
    while(theWorker.isAlive()){}
    //Make the worker object eligible for garbage
    // collection.
    theWorker = null;
    System.out.println(
         "Worker dead, eligible for garbage collection.");

    System.out.println(
                  "Final TextBox contents: " + savedText);

    System.out.println("MIDlet Destroyed");

    //It is assumed that the following method call will
    // cause the AMS to make the MIDlet object eligible
    // for garbage collection, which in turn will make the
    // Toggler object eligible for garbage collection.

    //Tell the AMS that the MIDlet is in the destroyed
    // state. It can then be launched again.
    notifyDestroyed();
  }//end destroyApp
  //----------------------------------------------------//

  //This method is called by the Toggler thread to
  // instruct the MIDlet to request that it be returned
  // to the active state.
  public void resume(){
    resumeRequest();
  }//end resume
  //====================================================//

  //This is a member class.  The purpose of an object of
  // this class is to cause the MIDlet to toggle between
  // the active state and the paused state for a total
  // of 2.5 cycles (3 active periods and 2 paused
  // periods). While the MIDlet is in the paused state,
  // this thread displays uneditable text on the screen.
  // The AMS also displays an Incoming Call message during
  // the time that the MIDlet is paused. After 2.5 cycles,
  // this thread instructs the MIDlet to enter the
  // destroyed state.
  class Toggler extends Thread{

    Toggler(){
      System.out.println("Toggler constructed");
    }//end constructor

    //Create an UNEDITABLE TextBox
    public void run(){
      System.out.println("Toggler Thread Started");
      TextBox textBox = new TextBox(
                                "Text Display by Toggler",
                                "",//text in TextBox
                                27,//width of TextBox
                                TextField.UNEDITABLE);

      //Loop for 2.5 cycles
      for(int cnt = 0;cnt < 5;cnt++){
        if(cnt % 2 != 0){
          //Tell the MIDlet to enter the paused state on
          // odd values of cnt.
          theMIDlet.pauseApp();

          //Now display some uneditable text while the
          // MIDlet is paused.
          //The TextBox must be wide enough to contain all
          // of the text.  Otherwise a
          // java/lang/IllegalArgumentException will be
          // thrown.
          textBox.setString(
                       "UNEDITABLE Toggler text: " + cnt);
          Display.getDisplay(theMIDlet).
                                      setCurrent(textBox);

          //Sleep for two seconds with the MIDlet in the
          // paused state. Then go back to the top of the
          // loop.
          try{Thread.currentThread().sleep(2000);
          }catch(Exception e){}

        }else{//The value of cnt is even.
          //Cause the thread to send a request to
          // the AMS to return it to the active
          // state.  If the request is honored, the
          // AMS will call the startApp method on the
          // thread. Note in particular that this
          // code does not call the startApp method
          // directly but delegates that action to the
          // AMS by way of the MIDlet.
          theMIDlet.resume();

          //Sleep for four seconds with the MIDlet in the
          // active state.  Then go back to the top of the
          // loop.
          try{Thread.currentThread().sleep(4000);
          }catch(Exception e){}
        }//end else
      }//end for loop

      //Instruct the MIDlet to enter the destroyed state
      // when control exits the loop and reaches this
      // point.
      theMIDlet.destroyApp(true);

      //This thread will die at this point.
    }//end run
  }//end class Toggler
  //====================================================//

  //This is a member class.  The purpose of an object of
  // this class is to do some work while the MIDlet is in
  // the active state and to become eligible for garbage
  // collection when the MIDlet enters the paused state.
  // It also sleeps some while the MIDlet is in the active
  // state to avoid consuming major computational
  // resources.  The work done by the thread is:
  //  - Create an empty TextBox when the thread is
  //    started.
  //  - Set the title for the TextBox.
  //  - Allow the user to enter text in the TextBox while
  //    the MIDlet is in the active state.
  //  - Save the contents of the TextBox when the MIDlet
  //    enters the paused state.
  //  - Display the current contents of the TextBox when
  //    the MIDlet enters the paused state.
  //  - Make the Worker object eligible for garbage
  //    collection when the MIDlet enters the paused
  //    state.
  //  - Create a new TextBox object when the MIDlet
  //    enters the active state.
  //  - Set the title for the new TextBox object.
  //  - Populate the new TextBox object with the saved
  //    text from the previous TextBox object.
  //  - Allow the user to enter text in the new TextBox
  //    while the MIDlet is in the active state.
  class Worker extends Thread{
    long nap = 1;//Sleep for one millisecond
    int width = 50;//TextBox width.
    String title = "Enter Text";//TextBox title
    TextBox textBox;

    Worker(){
      System.out.println("Worker constructed");
    }//end constructor

    public void run(){
      System.out.println("Worker Thread Started");
      //Create a new TextBox and populate it with the
      // text that was saved when the MIDlet entered
      // the paused state earlier. That variable
      // initially contains an empty string.
      textBox = new TextBox(title,
                            savedText,
                            width,
                            TextField.ANY);
      //Display the new TextBox so the user can edit
      // its contents.
      Display.getDisplay(theMIDlet).setCurrent(textBox);

      //Now loop while the user edits the TextBox contents
      while(true){
        if(kill || paused){//Check the flags
          //Save the contents of the TextBox, display
          // the contents, break out of the loop and die.
          savedText = textBox.getString();
          System.out.println(
                        "TextBox contents: " + savedText);
          break;
        }else{
          //The MIDlet is not in the paused state and is
          // not being destroyed. Sleep for a short period
          // of time and allow the user to edit the text
          // in the TextBox. Then loop back around and
          // check the flags again.
          try{
            Thread.currentThread().sleep(nap);
          }catch(InterruptedException e){
            System.out.println(
                            "Worker awakened from a nap");
          }//end catch
        }//end else

      }//end while loop

      //Control is transferred to here when the kill flag
      // or the pause flag is found to be true and a break
      // is executed inside the loop. The Worker thread
      // will die when the run method exits.
    }//end run
  }//end class Worker
  //====================================================//

}//end class SaveState01

 


Copyright

Copyright 2008, Richard G. Baldwin. Reproduction in whole or in part in any form or medium without express written permission from Richard Baldwin is prohibited.

About the author

Richard Baldwin is a college professor (at Austin Community College in Austin, TX) and private consultant whose primary focus is a combination of Java, C#, and XML. In addition to the many platform and/or language independent benefits of Java and C# applications, he believes that a combination of Java, C#, and XML will become the primary driving force in the delivery of structured information on the Web.

Richard has participated in numerous consulting projects and he frequently provides onsite training at the high-tech companies located in and around Austin, Texas. He is the author of Baldwin's Programming Tutorials, which have gained a worldwide following among experienced and aspiring programmers. He has also published articles in JavaPro magazine.

In addition to his programming expertise, Richard has many years of practical experience in Digital Signal Processing (DSP). His first job after he earned his Bachelor's degree was doing DSP in the Seismic Research Department of Texas Instruments. (TI is still a world leader in DSP.) In the following years, he applied his programming and DSP expertise to other interesting areas including sonar and underwater acoustics.

Richard holds an MSEE degree from Southern Methodist University and has many years of experience in the application of computer technology to real-world problems.

Baldwin@DickBaldwin.com






Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel