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

Back to Basics with MIDlets and the Sun Java Wireless Toolkit for CLDC

  • January 29, 2008
  • By Richard G. Baldwin
  • Send Email »
  • More Articles »

Java Programming Notes # 2574

Preface

This is the third lesson 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 (see Resources). The previous lesson was titled Capturing Output Produced by Programs Running in a Child Process.

A MIDlet development framework

In the first lesson I provided and explained a MIDlet development framework that makes experimenting with the programming of MIDlets fairly easy. In the previous lesson, I taught you how to capture and display the standard output and the error output produced by programs executing in a child process. I applied that knowledge to upgrade the MIDlet development framework from the earlier lesson.

What you will learn

In this lesson, I will tackle two topics. First, I will teach you how the CLDC and the MIDP fit into the grand scheme of things when programming MIDlets. Next, I will explain and demonstrate the life cycle of a MIDlet.

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. Output from program DateDemo02.
  • Figure 2. Output from program DateDemo01.
  • Figure 3. Output from the MIDlet named LifeCycle02.
  • Figure 4. Cell phone emulator output when MIDlet is in paused state.
  • Figure 5. Standard output from the MIDlet named LifeCycle01.

Listings

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.

How the CLDC and the MIDP fit into the grand scheme of things

What are the CLDC and the MIDP anyway?

Let's begin by taking a look at what some others have to say on this topic (see Resources).

According to Wikipedia

"The Connected Limited Device Configuration (CLDC) is a specification of a framework for Java ME applications targeted at devices with very limited resources such as pagers and mobile phones."

"Mobile Information Device Profile (MIDP) is a specification published for the use of Java on embedded devices such as mobile phones and PDAs. MIDP is part of the Java Platform, Micro Edition (Java ME) framework and sits on top of Connected Limited Device Configuration, a set of lower level programming interfaces."

According to Vikram Goyal (see Resources)

"J2ME can be divided into three parts... a configuration, a profile, and optional packages. A configuration contains the JVM (not the traditional JVM, but the cut-down version) and some class libraries; a profile builds on top of these base class libraries by providing a useful set of APIs ... The configuration and profile are supplied by the device manufacturers and they embed them in the devices."

Continuing with Goyal:

"The most popular profile and configuration that Sun provides are the Mobile Information Device Profile (MIDP) and Connected Limited Device Configuration (CLDC), respectively. As the name suggests, CLDC is for devices with limited configurations; for example, devices that have only 128 to 512KB of memory available for Java applications. Consequently, the JVM that it provides is very limited and supports only a small number of traditional Java classes. (This limited JVM is actually called the KVM.) ...

The MID profile ... provides the basic API that is used for creating application for these devices."

According to this author

Let me take a crack at the explanation in more fundamental and practical terms. You learned in a previous lesson that you can compile a MIDlet using a standard J2SE compiler (javac.exe). You compile the MIDlet by entering a command at the command line containing the terms shown in Listing 1. (All of the terms must be on a single line to constitute a valid command.)

Listing 1. Command for compiling a MIDlet.

javac -target 1.4 -source 1.4 
-bootclasspath 
C:\WTK2.5.2\lib\cldcapi11.jar;
C:\WTK2.5.2\lib\midpapi20.jar 
WTK001\WTK001.java

In Listing 1, cldcapi11.jar and midpapi20.jar are the names of two JAR files containing class files in the lib directory of the installation directory tree for the wireless toolkit, WTK2.5.2. These two JAR files correspond to CLDC 1.1 and MIDP 2.0 respectively.

Other versions are available

These are not the only JAR files corresponding to the CLDC and the MIDP in the wireless toolkit. The lib directory of WTK2.5.2 contains the following JAR files corresponding to these and other versions of the CLDC and the MIDP:

  1. cldcapi10.jar
  2. cldcapi11.jar
  3. midpapi10.jar
  4. midpapi20.jar
  5. midpapi21.jar

Links to online API documentation for the first four items in the above list are provided in Resources.

The bootclasspath option

The bootclasspath option to javac (-bootclasspath in Listing 1) instructs the compiler to "Override the location of bootstrap class files." In other words, as used in Listing 1, this option instructs the compiler to forgo the use of the standard J2SE class libraries and to use the class libraries provided by the wireless toolkit instead.

The two elements in the classpath specify the paths to the WTK2.5.2 JAR files for CLDC 1.1 and MIDP 2.0 respectively. Thus, the classes in those two JAR files will be used to compile the program instead of the classes in the standard J2SE class library.

If you want to compile to a different version of the CLDC and/or the MIDP, substitute the names of different JAR files from the above list in the command shown in Listing For example, the cell phone that I carry in my pocket has CLDC 1.0 and MIDP 1.0 installed. Therefore, it would not support some of the features in the later versions of the CLDC and the MIDP.

What class files are provided by CLDC 1.1?

Now that we have eliminated the class files from the standard J2SE class libraries, it is useful to know what class files we have to work with. According to the API documentation (see Resources), CLDC 1.1 contains the following packages:

  • java.io
  • java.lang
  • java.lang.ref
  • java.util
  • javax.microedition.io

Common package names

The three packages shown in boldface have exactly the same names as packages in JDK 1.6. Thus, they are direct but smaller replacements for the packages in the standard J2SE class library having the same names.

Consider the java.io package

Consider, for example, the java.io package. This package contains the following eleven classes in CLDC 1.1:

  1. ByteArrayInputStream
  2. ByteArrayOutputStream
  3. DataInputStream
  4. DataOutputStream
  5. InputStream
  6. InputStreamReader
  7. OutputStream
  8. OutputStreamWriter
  9. PrintStream
  10. Reader
  11. Writer

On the other hand, if I counted correctly, the java.io package in JDK 1.6 contains more than fifty classes, including all eleven of the classes in the above list.

What about the methods in the classes?

Drilling down even deeper, the Reader class in CLDC 1.1 contains only nine methods whereas the Reader class in JDK 1.6 contains ten methods.

What about the package names that don't match?

The two packages in the above list that are contained in CLDC 1.1 but are not contained in JDK 1.6 contain only three classes: Connector, Reference, and WeakReference.

I don't believe that JDK 1.6 contains a class named Connector. JDK 1.6 does contain several different classes named Reference and several different classes named WeakReference in different packages.

To the extent that the wireless toolkit classes have the same package and class names as their counterparts in JDK 1.6, those packages and classes exist in three packages in the CLDC portion of the wireless class library.

The MIDP class library

Curiously, those same three matching packages plus the package named javax.microedition.io are also contained in MIDP 2.0 and in some cases, the version in MIDP 2.0 contains more classes than the version in CLDC 1.1. For example, the following two classes are contained in the java.util package in MIDP 2.0 but are not contained in the java.util package in CLDC 1.1:

  1. java.util.Timer
  2. java.util.TimerTask

In a similar vein, the javax.microedition.io package in MIDP contains a class named PushRegistry that is not contained in the package having the same name in CLDC 1.1.

A quick scan indicates that other than the three classes mentioned above, the four packages that are common to CLDC 1.1 and MIDP 2.0 have the same number of classes with the same names. However, I haven't drilled deep enough to determine if any of the classes that are common between CLDC 1.1 and MIDP 2.0 have different methods.

What about the other packages in MIDP 2.0?

MIDP 2.0 contains the following packages:

  1. java.io
  2. java.lang
  3. java.util
  4. javax.microedition.io
  5. javax.microedition.lcdui
  6. javax.microedition.lcdui.game
  7. javax.microedition.media
  8. javax.microedition.media.control
  9. javax.microedition.midlet
  10. javax.microedition.pki
  11. javax.microedition.rms

Except for the first three packages (highlighted in boldface), packages having these names are not contained in JDK 1.6. Therefore, the classes in these packages were probably designed specifically for use with MIDlets.

The bottom line

In the most basic sense, the CLDC and the MIDP are simply a pair of JAR files containing class files that are used in place of the class files from the standard J2SE class library to compile and execute MIDlet code.

The total number of class files in the CLDC and the MIDP is much lower than the number of class files in the standard J2SE class library.

The class files in the CLDC and the MIDP are specially designed to be run in devices with limited configurations under the control of a scaled down virtual machine.

Different versions of the CLDC and the MIDP are available and you must specify the versions that you are targeting when you compile a MIDlet.

A virtual machine, a CLDC, and an MIDP are embedded in a Java-compatible device by the manufacturer of the device. Therefore, it isn't necessary to provide the class files and the virtual machine to deploy a MIDlet into a cell phone. However, it is necessary to make certain that the versions of the class files and the virtual machine used to develop and test the MIDlet are compatible with the versions that are embedded in the cell phone.

A simple example

Windows versus other platforms
The batch file used in this example is written in Windows format. Hopefully you can write a comparable script that is compatible with your platform.

An important characteristic of Java

Before getting into the life cycle of a MIDlet, I want to show you a pair of simple example programs to refresh you memory on an important characteristic of Java. This is something that you probably already knew, but may have forgotten about, or may never have thought much about. That characteristic is:

You can completely change the behavior of a Java program by replacing one or more classes in the standard class library with custom classes of your own design.

Create some files

Use your text editor to create the two simple Java programs and the batch file shown in Listings 2 through 4 in the current directory. Then execute the batch file.

Listing 2. DateDemo01.java.

/*DateDemo01.java
Tested using Java SE 6 running under Windows XP.
*********************************************************/
import java.util.*;

class DateDemo01{
  public static void main(String[] args){
    System.out.println(new Date());
  }//end mail
}//end DateDemo01

class Date{
  public String toString(){
    return "A custom Date class.";
  }//end overridden toString method

}//end substitute class named Date

 

Listing 3. DateDemo02.java.

/*DateDemo02.java
Tested using Java SE 6 running under Windows XP.
*********************************************************/
import java.util.*;

class DateDemo02{
  public static void main(String[] args){
    System.out.println(new Date());
  }//end mail
}//end DateDemo02

 

Listing 4. Batch file to demonstrate class substitution.

echo off
echo Demonstrate class substitution

del *.class
javac -classpath .;  DateDemo02.java
dir /B *.class
java -classpath .;  DateDemo02

del *.class
javac -classpath .; DateDemo01.java
dir /B *.class
java -classpath .;  DateDemo01

pause

The behavior of the batch file

The batch file in Listing 4 begins by:

  • Deleting all of the class files in the current directory.
  • Compiling the program named DateDemo02 shown in Listing 3.
  • Displaying all of the class files in the current directory.
  • Executing the program named DateDemo02.

Output from program DateDemo02

This produces the output shown in Figure 1.

Figure 1. Output from program DateDemo02.

Demonstrate class substitution
DateDemo02.class
Sun Dec 09 09:16:25 CST 2007

As you can see in Figure 1, following compilation of the program named DateDemo02, the current directory contains only one class file and it is named DateDemo02.class.

As you can also see in Figure 1, execution of the program named DateDemo02 produces an output consisting of the current date and time. This is what you would normally expect from the single statement in the main method in DateDemo02, which

  • Instantiates a new object of the Date class in the standard class library by calling the constructor that takes no parameters.
  • Passing that object's reference to the println method.

So far, everything is as you would normally expect when you execute the code in the main method in the program named DateDemo02.

The program named DateDemo01

Now consider the behavior of the program named DateDemo01 shown in Listing 2. Note in particular that the main method in DateDemo01 is identical to the main method in DateDemo02.

The batch file in Listing 4 continues by:

  • Deleting all of the class files in the current directory again.
  • Compiling the program named DateDemo01 shown in Listing 2.
  • Displaying all of the class files in the current directory.
  • Executing the program named DateDemo01.

Output from program DateDemo01

This produces the output shown in Figure 2.

Figure 2. Output from program DateDemo01.

Date.class
DateDemo01.class
A custom Date class.

The first thing to note about the output shown in Figure 2 is that the current directory now contains a class file named Date.class in addition to the class file named DateDemo01.class. This results from the fact that a simple class named Date is included in Listing 2 in addition to the class named DateDemo01.

The next thing to notice is that the output shown in Figure 2 doesn't display the current date and time.  Instead, it displays the text shown in boldface in Figure 2.

How did this happen?

This happened as a result of:

  • The inclusion of the definition of the class named Date in Listing 2.
  • The inclusion of the "-classpath" option shown in boldface in the batch file in Listing 4.

What is the "-classpath" option?

The "-classpath" option is described in one of the following two ways, depending on whether you are looking at the documentation for java or the documentation for javac respectively:

  • class search path of directories and zip/jar files
  • Specify where to find user class files and annotation processors

Both descriptions of the "-classpath" option mean essentially the same thing. The classpath that you provide along with this option tells the compiler or the virtual machine where to look for class files before looking for them in the standard library. If a suitable class file is found in one of the locations specified by the classpath, the compiler or the virtual machine will look no further but will attempt to use that class file to either compile or execute the program, depending on whether javac.exe or java.exe is being executed.

The current directory

In this case, the classpath used in the batch file in all four cases was ".;" (this is a period followed by a semicolon in case it is not clear in the rendering of this document on your machine). This tells the compiler or the virtual machine to look in the current directory before looking in the standard library for all class files that are needed.

The main method in both programs required the compiler and the virtual machine to find and use a class named Date that has a constructor that takes no parameters. In the case of compiling and executing the program named DateDemo02, there was no local definition of a class named Date and no file named Date.class in the current directory. Therefore, the compiler and the virtual machine retrieved and used the class file named Date.class from the standard library, producing the current date and time in the output shown in Figure 1.

In the second case involving the program named DateDemo01, the compiler found the definition for a suitable class named Date in the file with the definition of the class named DateDemo01. It compiled this Date class and wrote the resulting class file named Date.class in the current directory. As a result, the current directory ended up containing a file named Date.class. That is the class file that was used by the virtual machine to execute the program, producing the output shown in Figure 2.

The bottom line

The bottom line is that you can completely change the behavior of a Java program by substituting custom class files in place of the class files in the standard library.

Furthermore, this doesn't require you to perform major surgery on the JAR files that contain the standard library. All that is required is that you cause the compiler and the virtual machine to find your custom classes before they search the standard library for the required class files. This can be accomplished through the manipulation of the classpath environment variable.

In this example, I only wanted to replace one class files, so I simply placed the replacement class file in the current directory and manipulated the classpath to cause the virtual machine to search the current directory before searching the standard library. As a result, it found and used the version of the required class file in the current directory and didn't search the standard library for that class file.

The J2ME on the other hand replaces all of the classes in the standard library with a smaller set of custom classes. It accomplishes this by using the "-bootclasspath" option with the compiler to "Override the location of bootstrap class files" and substitutes a complete new class library contained in two JAR files as shown in Listing 1.

The life cycle of a MIDlet

General background information

When you write your code for a new MIDlet, the main class must extend the javax.microedition.midlet.MIDlet class. Otherwise, you are very likely to encounter compiler errors.

The main class ...
As used here, the term main class refers to the class that will be instantiated by the Application Management Software (AMS) to instantiate the object that constitutes the MIDlet in the cell phone.

Methods of the MIDlet class

The MIDlet class in MIDP 2.0 defines nine methods including the six methods shown below:

  • protected abstract void startApp()
  • protected abstract void pauseApp()
  • public final void notifyPaused()
  • protected abstract void destroyApp(boolean unconditional)
  • public final void notifyDestroyed()
  • public final void resumeRequest()

As you can see from the above list, three of the methods are abstract. Whenever you extend a class containing abstract methods, you must provide concrete definitions of those abstract methods, or you must declare your new class abstract meaning that it cannot be instantiated.

Instantiating the MIDlet class

At some point during the process of deploying a MIDlet into a cell phone, the cell phone instantiates an object from your main MIDlet class. Therefore, you must provide concrete definitions of the three abstract methods in the above list.

According to Goyal,

"Mobile devices, whether emulators or real, interact with a MIDlet using their own software, which is called Application Management Software (AMS). The AMS is responsible for initializing, starting, pausing, resuming, and destroying a MIDlet. (Besides these services, AMS may be responsible for installing and removing a MIDlet, as well.) To facilitate this management, a MIDlet can be in one of three states which is controlled via the MIDlet class methods, that every MIDlet extends and overrides. These states are active, paused and destroyed."

The three states of a MIDlet

As described by Goyal, a MIDlet can exist in any one of three states:

  1. paused
  2. active
  3. destroyed

The MIDlet can be switched back and forth between the paused and active states either by the AMS or by code written into the MIDlet. The MIDlet can enter the destroyed state from either the paused state or from the active state.

Apparently the required behavior of the states is not enforced

A first reading of the literature may cause you to believe that the required behavior of these states is enforced by some higher power. In other words, you might conclude that a MIDlet in the destroyed state cannot execute code (much like a thread that has gone to sleep cannot execute code). While that may be true for actual cell phones, it is certainly not true for MIDlet code executing in Sun's cell phone emulator. As I will show later, a MIDlet in the destroyed state running in the emulator can continue to execute code just as though it never entered the destroyed state in the first place.

Control over MIDlet state

Therefore, the control over MIDlet state that is provided by theMIDlet class methods should be considered somewhat analogous to the control over automobile drivers that is provided by traffic lights. Drivers are expected to follow a set of well-defined rules when approaching a traffic light, but there is nothing to prevent a driver from speeding right through a red light. For example, the requirement for the driver to stop when the traffic light turns red is not enforced by a large gate coming down and blocking the road, as is the case at many railroad crossings and ferry landings.

Output from the MIDlet named LifeCycle02

This lack of enforcement is illustrated in Figure 3,which shows a portion of the output from a MIDlet named LifeCycle02. (Four lines of text in Figure 3 were manually made boldface by the author for emphasis.)

Figure 3. Output from the MIDlet named LifeCycle02.

OUT: Running with locale: English_United States.1252
OUT: Running in the identified_third_party security domain
OUT: Constructed at: 0
OUT: startApp method called at: 40
OUT:
OUT: pauseApp method called at: 50
OUT: Time is: 1052
OUT: Time is: 2053
OUT: Time is: 3055
OUT: Time is: 4056
OUT: Time is: 5058
OUT: destroyApp method called at:  5058
OUT: Time is: 6059
OUT: Time is: 7060
OUT: Time is: 8062
OUT: Time is: 9063
OUT: Time is: 10065

Display time that certain events occur

I will present and explain the code in the MIDlet named LifeCycle02 later. For now, suffice it to say that the MIDlet displays the time in milliseconds that certain events happen during the execution of the MIDlet (relative to the time that the MIDlet object was constructed). The output for four of these events is highlighted in boldface in Figure 3.

The MIDlet object was constructed at time zero. The startApp method was called by the AMS at 40 milliseconds following construction.

A call to the pauseApp method

The pauseApp method was called by the startApp method at 50 milliseconds following construction. The pauseApp method immediately notified the AMS that it was entering the paused state by calling the notifyPaused method. Then it executed another statement to produce the third boldface line of text in Figure 3 even though it was supposed to be in the paused state. Then it returned control to the startApp method.

A call to the destroyApp method

When the startApp method received control, it continued to execute code even though the MIDlet was supposed to be in the paused state. Note that it wasn't restarted. It simply continued with the next statement following the earlier call to the pauseApp method.

At this point, the startApp method executed the code necessary to produce five more lines of output text and then called the destroyApp method.

The destroyApp method notified the AMS that it was entering the destroyed state by calling the method named notifyDestroyed. Then it executed another statement to produce the fourth line of boldface text in Figure 3 even though the MIDlet was supposed to be in the destroyed state at that point in time. Then the destroyApp method returned control to the startApp method.

Execute some more code

Once again, when the startApp method received control, it continued to execute code even though the MIDlet was supposed to be in the destroyed state. It executed the code necessary to produce five more lines of output text before returning control to the AMS from which it was called it in the first place.

Not how it is supposed to be done

This is not how it is supposed to be done. This MIDlet clearly failed to follow the rules and ran several red lights during its execution.

Programmers are expected to follow the rules

Unlike the author of the MIDlet named LifeCycle02 (yours truly), programmers using the MIDlet class methods to control the state of the MIDlet are expected to follow a set of rules, which are reasonably well defined in the documentation. If a programmer fails to follow the rules, chaos may ensue when the MIDlet is actually executed in a cell phone or cell phone emulator. However, there appears to be nothing to force that programmer to follow the rules; at least that is true for Sun's cell phone emulator.

How the MIDlet class methods are supposed to be used

No constructor parameters allowed
It is my understanding that the constructor for the MIDlet may not have any arguments. This makes sense due to the fact that if the constructor did have arguments, the AMS would have no way of knowing what those arguments are. (A simple test will probably demonstrate that if you include arguments in your MIDlet constructor, it will throw a runtime error or exception.)

That having been said, let's discuss how the five methods in the above list are supposed to be used to control the state of a MIDlet.

It appears that when a MIDlet is installed in a cell phone, the JAR file containing the class files is saved by the cell phone. Then when the MIDlet is launched, the AMS instantiates a new object of the MIDlet class by applying the new operator to the constructor for the class in the normal way. (The term launched comes from the Sun cell phone emulator. Other emulators or actual cell phones may use different terminology for this process.)

Starts out in the paused state

When the object is instantiated from your MIDlet class, the MIDlet starts out in the paused state. (This probably occurs when you click the soft key on the cell phone keypad to launch the MIDlet.) Shortly thereafter, the AMS will call the startApp method on the MIDlet object. This provides the first opportunity for the MIDlet to execute code. According to the Sun documentation, calling the startApp method.

"Signals the MIDlet that it has entered the Active state. In the Active state the MIDlet may hold resources. The method will only be called when the MIDlet is in the Paused state."

The terminology is misleading

Once again, the terminology is somewhat misleading. There appears to be nothing to prevent the programmer from calling the startApp method when the MIDlet is in the active state. Experimentation shows that this does not produce a compiler error and does not necessarily produce an immediate runtime error in the Sun cell phone emulator. Of course, it does put the MIDlet into an infinite recursive loop from which it probably can't escape of its own accord, which is not a good thing. It would probably be more correct if the above text were to read, "should only be called."

The resumeRequest method

The startApp method probably should not be called by code executing in the MIDlet, even when the MIDlet is in the paused state. Instead, the method named resumeRequest should be called. If the AMS agrees that the MIDlet should be moved from the paused stat to the active state at that point in time, it will call the startApp method on the MIDlet object.

Here is part of what Sun has to say about the resumeRequest method:

"Provides a MIDlet with a mechanism to indicate that it is interested in entering the Active state. Calls to this method can be used by the application management software to determine which applications to move to the Active state.

When the application management software decides to activate this application it will call the startApp method."

Executing code while in the paused state

You might wonder how the code in a MIDlet can call the resumeRequest method when it is in the paused state and is not supposed to be executing code. Here is some more of what Sun has to say about the resumeRequest method:

"The application is generally in the Paused state when this is called. Even in the paused state the application may handle asynchronous events such as timers or callbacks."

In other words, even though a paused MIDlet is supposed to have released certain resources and is not generally supposed to be executing code, it is not supposed to be dead or completely deaf. It should be able to service "asynchronous events such as timers or callbacks."

Entering the paused state

Either the AMS or code running in the MIDlet can cause the MIDlet to enter the paused state by calling the pauseApp method on the MIDlet object. Here is some of what Sun has to say about the pauseApp method:

"Signals the MIDlet to enter the Paused state. In the Paused state the MIDlet must release shared resources and become quiescent. This method will only be called when the MIDlet is in the Active state."

Clarification of the text

Once again, in order to reflect the reality of non-enforcement, it would probably be better if the terms "must release" and "will only be called" were replaced by "should release" and "should only be called."

The notifyPaused method

To satisfy the spirit of the requirements, the overridden pauseApp method should not only "release shared resources" and "become quiescent"; it should also call the method named notifyPaused, which notifies the AMS that the MIDlet is playing by the rules. Here is part of what Sun has to say about the notifyPaused method:

"Notifies the application management software that the MIDlet does not want to be active and has entered the Paused state. Invoking this method will have no effect if the MIDlet is destroyed, or if it has not yet been started.

It may be invoked by the MIDlet when it is in the Active state.

If a MIDlet calls notifyPaused(), in the future its startApp() method may be called make it active again, or its destroyApp() method may be called to request it to destroy itself.

If the application pauses itself it will need to call resumeRequest to request to reenter the active state."

Not an accurate reflection of reality

Once again, I'm not certain that the first paragraph in the above quotation is an accurate reflection of reality. In fact, the MIDlet may have a great desire to remain active but the AMS may have called the pauseApp method on behalf of some higher priority activity such an incoming call on the cell phone. I view the act of calling the notifyPaused method on the part of the MIDlet as simply being an acknowledgement that:

  • Its pauseApp method has been called
  • It will play by the rules
  • It will (possibly grudgingly) release shared resources and become quiescent

Entering the destroyed state

That brings us to the final two methods of immediate interest in the MIDlet class:

  • protected abstract void destroyApp(boolean unconditional)
  • public final void notifyDestroyed()

The destroyApp method

The destroyApp method is a little more complicated than the other methods in the earlier list. Here is part of what Sun has to say about this method:

"Signals the MIDlet to terminate and enter the Destroyed state. In the destroyed state the MIDlet must release all resources and save any persistent state. This method may be called from the Paused or Active states.

MIDlets should perform any operations required before being terminated, such as releasing resources or saving preferences or state.

Note: The MIDlet can request that it not enter the Destroyed state by throwing an MIDletStateChangeException. This is only a valid response if the unconditional flag is set to false. If it is true the MIDlet is assumed to be in the Destroyed state regardless of how this method terminates. If it is not an unconditional request, the MIDlet can signify that it wishes to stay in its current state by throwing the MIDletStateChangeException. This request may be honored and the destroyApp() method called again at a later time."

Asking for a reprieve

As you can see from the above quotation, unlike the pauseApp method, the MIDlet can ask for a reprieve from entering the destroyed state by throwing a MIDletStateChangeException if the incoming parameter is false. However, this opportunity for a reprieve is not available if the incoming parameter is true.

The calling method may grant the reprieve or it may call the destroyApp method again, this time with a true parameter indicating that the reprieve is not granted. However, it is apparently assumed that the MIDlet has not entered the destroyed state if the incoming parameter is false and the destroyApp method throws the MIDletStateChangeException.

The notifyDestroyed method

According to Sun, this method is

"Used by an MIDlet to notify the application management software that it has entered into the Destroyed state. The application management software will not call the MIDlet's destroyApp method, and all resources held by the MIDlet will be considered eligible for reclamation. The MIDlet must have performed the same operations (clean up, releasing of resources etc.) it would have if the MIDlet.destroyApp() had been called."

When should this method be called?

This method should be called if the MIDlet elects to enter the destroyed state of its own accord without its destroyApp method having been called. However, it is unclear to me whether or not the MIDlet should also call this method:

  • When its destroyApp method has been called.
  • When either the incoming parameter is true, or the MIDlet has elected not to throw a MIDletStateChangeException.
  • When it has completed all cleanup activities, and is ready to enter the destroyed state.

However, my assumption is that calling the notifyDestroyed method at this point may be useful to the AMS and probably wouldn't be harmful. The AMS should be able to interpret this call as a signal that the MIDlet has entered the destroyed state as requested.

Preview

I will present and explain two MIDlet programs in this lesson:

  • LifeCycle02
  • LifeCycle01

The MIDlet named LifeCycle02 produces the output shown in Figure 3 and demonstrates that the required behavior for each of the three states of a MIDlet is not enforced.

Given that state behavior is not enforced, the MIDlet named LifeCycle01 illustrates one approach to writing MIDlet code that plays by the rules.

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, (which includes the Sun cell phone emulator) running under Windows XP.

Discussion and sample code

The MIDlet named LifeCycle02

This MIDlet demonstrates that a MIDlet can continue to execute code while in the paused state or in the destroyed state when running in the Sun cell phone emulator.

During the time that the MIDlet is in the paused state, the Sun cell phone emulator displays a box on the cell phone screen as shown in Figure 4. This is an example of what is often referred to as a system screen.

Figure 4. Cell phone emulator output when MIDlet is in paused state.

In addition, as shown in Figure 4, a red indicator shows in the middle of the right-most soft key while the MIDlet is in the paused state. Despite this, however, this MIDlet continues to execute code and to display text on the standard output screen while it is in the paused state or the destroyed state. The text that the MIDlet displays on the standard output device is shown in Figure 3.

Will explain in fragments

I will explain the code in this MIDlet in fragments, the first of which is shown in Listing 5.  (A complete listing of the MIDlet program is provided in Listing 25.)

Listing 5. Beginning of LifeCycle02 MIDlet program.

package LifeCycle02;

import javax.microedition.midlet.MIDlet;
import java.lang.Thread;
import java.util.Date;

public class LifeCycle02 extends MIDlet{

  long timeBase;
  boolean running = false;

  public LifeCycle02(){
    timeBase = new Date().getTime();
    System.out.println("Constructed at: "
                       + (new Date().getTime()-timeBase));
  }//end constructor

Listing 5 shows the beginning of the MIDlet class including the constructor. The code in the constructor establishes the time (in milliseconds relative to Jan 1, 1970) that the MIDlet object is constructed. This time will be used to compute the elapsed times in milliseconds at which other events occur relative to the construction time of the MIDlet.

Beginning of the startApp method

The beginning of the method named startApp is shown in Listing 6.

Listing 6. Beginning of the startApp method.

  public void startApp(){

    System.out.println("startApp method called at: "
                       + (new Date().getTime()-timeBase));
    //Make the AMS believe that the MIDlet is paused
    pauseApp();

The startApp method is initially called by the AMS to change the state of the MIDlet from paused to active.

The pauseApp method

The code in the startApp method in Listing 6 calls the pauseApp method to cause the AMS to believe that the MIDlet is in the paused state. Putting the startApp method on the back burner for a little while, Listing 7 shows the pauseApp method in its entirety.

Listing 7. The pauseApp method.

  public void pauseApp(){
    notifyPaused();
    System.out.println("\npauseApp method called at: "
                       + (new Date().getTime()-timeBase));
  }//end pauseApp

The pauseApp method begins by calling the notifyPaused method. This erroneously notifies the AMS that the MIDlet will follow the rules, go into the paused state, and will release shared resources and become quiescent. However, the MIDlet does not become quiescent. Rather, the pauseApp method immediately executes another statement, which produces the third line of boldface text shown in Figure 3. Then the pauseApp method returns control to the startApp method from which it was called.

The startApp method continues executing code

Rather than being quiescent, when control returns to the startApp method, it continues executing code as shown in Listing 8.

Listing 8. The startApp method continues executing code.

    for(int cnt = 0;cnt < 5;cnt++){
      try{Thread.currentThread().sleep(1000);
      }catch(InterruptedException e){}
      System.out.println("Time is: "
                       + (new Date().getTime()-timeBase));
    }//end for loop
    
    //Destroy the MIDlet.
    destroyApp(true);

The code shown in Listing 8 produces the five lines of text following the third line of boldface text in Figure 3.

The destroyApp method

Then the startApp method calls the method named destroyApp as shown by the last statement in Listing 8. The destroyApp method is shown in its entirety in Listing 9.

Listing 9. The destroyApp method.

  public void destroyApp(boolean unconditional){
    notifyDestroyed();
    System.out.println("destroyApp method called at:  "
                       + (new Date().getTime()-timeBase));
  }//end destroyApp

The destroyApp method immediately calls the notifyDestroyed method to erroneously tell the AMS that the MIDlet is in the destroyed state. Once it is in the destroyed state, it can then be launched again.

Then, rather than acting like the MIDlet is in the destroyed state, the destroyApp method executes another statement, which produces the fourth line of boldface text in Figure 3. Then the destroyApp method returns control to the startApp method from which it was called.

The startApp method executes even more code

When control returns to the startApp method, it executes even more code as shown in Listing 10 even though the MIDlet is supposed to be in the destroyed state. This produces the last five lines of output text shown in Figure 3.

Listing 10. The startApp method executes even more code.

    for(int cnt = 0;cnt < 5;cnt++){
      try{Thread.currentThread().sleep(1000);
      }catch(InterruptedException e){}
      System.out.println("Time is: "
                       + (new Date().getTime()-timeBase));
    }//end for loop
  }//end startApp
}//end class LifeCycle02

Finally, after executing that code, which requires about five seconds, the startApp method returns control to the AMS from which it was called and the MIDlet acts as though it is in the destroyed state.

Not how it should be done

The MIDlet named LifeCycle02 demonstrates how you should not use the methods of the MIDlet class to control the state of the MIDlet. The code in this MIDlet simply doesn't follow the rules of the road. However, the MIDlet program named LifeCycle01, which I will present and explain next does follow the rules of the road and does show how I believe the methods of the MIDlet class should be used to control the state of the MIDlet.

The MIDlet named LifeCycle01

The purpose of this MIDlet is to demonstrate the following three states in which a MIDlet can reside:

  • paused
  • active
  • destroyed

Three threads

This MIDlet is comprised of three threads. One thread is the main MIDlet thread containing the methods startApp, pauseApp, and destroyApp.

A second thread is instantiated from a Thread class named Toggler. This thread is spawned and run to toggle the MIDlet between the active and paused states at approximately two-second intervals. After approximately twenty seconds, the Toggler instructs the MIDlet to enter the destroyed state.

A third thread is instantiated from a Thread class named Worker. This thread is spawned and run to do some work while the MIDlet is in the active state. The work that it does is to display a period on the standard output device approximately once every half second. This thread sleeps while the MIDlet is in the paused state, and dies when the MIDlet enters the destroyed state. (It also sleeps some of the time that the MIDlet is in the active state to avoid consuming excessive computer resources.)

Cell phone output from the MIDlet

During those time intervals that the MIDlet is in the paused state, the Sun cell phone emulator displays the image shown in Figure 4. Because the Toggler repeatedly toggles the MIDlet between the active state and the paused state, the cell phone emulator screen repeatedly toggles between a blank screen (except for the name of the MIDlet at the top of the screen) and the image shown in Figure 4. When the MIDlet enters the destroyed state, the cell phone emulator screen goes blank and stays blank until the MIDlet is launched again.

Standard output from the MIDlet

Figure 5 shows the output produced by the MIDlet on the standard output device while the MIDlet is running. (Note that this output does not appear on the cell phone screen.)

Figure 5. Standard output from the MIDlet named LifeCycle01.

OUT: Constructed at: 0
OUT: Started at: 47
OUT: I'm working .....
OUT: Paused at: 2047
OUT: Re-started at: 4047
OUT: I'm awake .....
OUT: Paused at: 6047
OUT: Re-started at: 8047
OUT: I'm awake .....
OUT: Paused at: 10047
OUT: Re-started at: 12047
OUT: I'm awake .....
OUT: Paused at: 14047
OUT: Re-started at: 16047
OUT: I'm awake .....
OUT: Paused at: 18047
OUT: Destroyed at:  20047
OUT: I'm awake Terminating worker

What is the boldface text in Figure 5?

Some of the text in Figure 5 was manually highlighted in boldface by this author when this tutorial was written. The boldface text shows the standard output produced by the Worker thread while the MIDlet is in the active state.

It is important to note that there is no output produced by the Worker thread while the MIDlet is in the paused state because it has been put to sleep. There is also no output produced by the Worker thread while the MIDlet is in the destroyed state, except that when the worker wakes up and discovers that the MIDlet is in the destroyed state, the worker thread notifies us of that fact and terminates itself by causing itself to die a natural death.

Will discuss in fragments

I will present and explain this MIDlet program in fragments. You can view a complete program listing in Listing 26 near the end of the lesson.

Beginning of the class named LifeCycle01

The first fragment, which shows the beginning of the class named LifeCycle01 is shown in Listing 11.

Listing 11. Beginning of the class named LifeCycle01.

package LifeCycle01;

import javax.microedition.midlet.MIDlet;
import java.lang.Thread;
import java.util.Date;

public class LifeCycle01 extends MIDlet{

  Toggler theToggler;
  Worker theWorker;
  long timeBase;
  boolean running = false;

  public LifeCycle01(){
    theToggler = new Toggler(this);
    theWorker = new Worker(true,false);
    //Establish the time (in milliseconds relative to
    // Jan 1, 1970) that the MIDlet object is constructed.
    // This time will be used to compute the elapsed times
    // in milliseconds at which other events occur
    // relative to the construction time of the MIDlet.
    timeBase = new Date().getTime();
    System.out.println("Constructed at: "
                       + (new Date().getTime()-timeBase));
  }//end constructor

Given the previous discussion of the overall behavior of the MIDlet and comments in Listing 11, you should have no difficulty understanding the code in Listing 11.

The beginning of the startApp method

Listing 12 shows the beginning of the startApp method. This method is called by the AMS to change the state of the MIDlet from its initial paused state to the active state.

Listing 12. The beginning of the startApp method.

  public void startApp(){
    if(!running){
      //This is the first time that this method has been
      // called.
      System.out.println("Started at: "
                       + (new Date().getTime()-timeBase));
      running = true;
      //Start the Toggler thread running.
      theToggler.start();
      //Start the worker thread running in an active
      // (not-paused) MIDlet state.
      theWorker.paused = false;
      theWorker.start();

The running flag

The boolean variable named running is used to determine if the MIDlet is being moved to the active state for the first time or being moved from the paused state to the active state after having been paused earlier while the MIDlet was already running. Basically this variable is used:

  • To control which message to display on the standard output device:
    • Started at ..., or
    • Re-started at ...
  • To decide whether to:
    • Start the Toggler thread running or not.
    • Start the Worker thread running for the first time, or
    • To wake the Worker thread up from its self-imposed sleep.

The code in Listing 12 discovers that the running flag is false. As a result, it:

  • Displays a message telling when the MIDlet was first started (put into the active state).
  • Sets the running flag to true.
  • Starts the Toggler thread running.
  • Sets the paused flag belonging to the Worker thread to false.
  • Starts the Worker thread running for the first time.

When the running flag is true...

Listing 13 shows the else clause associated with the if-else construct that began in Listing 12.

Listing 13. When the running flag is true.

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

The code in Listing 13 is executed when the running flag is found to be true. This will be the case for the remainder of the execution of the MIDlet after the code in Listing 12 is executed once.

Wake up and get to work!

The code in Listing 13:

  • Displays a message showing the relative time that the MIDlet is being restarted (entering the active state) after having earlier been put in the paused state during the execution of the MIDlet.
  • Sets the paused flag belonging to the Worker thread to false, indicating that the thread should start working.
  • Wakes the Worker thread up. (It should have been sleeping while the MIDlet was in the paused state).

The pauseApp method

Listing 14 shows the pauseApp method in its entirety. 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 if the AMS decides that the MIDlet needs to enter the paused state for any reason (such as to release resources to support an incoming call).

Listing 14. The pauseApp method.

  public void pauseApp(){
    System.out.println("\nPaused at: "
                       + (new Date().getTime()-timeBase));
    //Tell Worker to go to sleep for a long time the next
    // time it checks the paused flag.
    theWorker.paused = true;
    //Tell the AMS that this MIDlet is in the paused
    // state.
    notifyPaused();
  }//end pauseApp

Behavior of the pauseApp method

The pauseApp method begins by setting the paused flag belonging to the Worker thread to true. The next time the Worker thread wakes up and checks this flag, it will put itself to sleep for a very long time.

Then the pauseApp method calls the notifyPaused method to tell the AMS that the MIDlet is playing by the rules. In particular, it has released shared resources and will become quiescent. The only remaining activity on the part of the MIDlet while it is in the paused state will be a very small amount of activity on the part of the Worker thread while it is checking its paused flag and putting itself to sleep for a long time.

The destroyApp method

Listing 15 shows the destroyApp method in its entirety. This thread is called by the Toggler thread to cause the MIDlet to enter the destroyed state. It is also possible that this method could be called by the AMS if the AMS decides for some reason to put the MIDlet into the destroyed state.

Listing 15. The destroyApp method.

  public void destroyApp(boolean unconditional){
    //Tell the Worker to terminate the next time it checks
    // its flags.
    theWorker.kill = true;
    //Wake the worker up if it is asleep.
    theWorker.interrupt();
    System.out.println("Destroyed at:  "
                       + (new Date().getTime()-timeBase));
    //Tell the AMS that the MIDlet is in the destroyed
    // state. It can then be launched again.
    notifyDestroyed();
  }//end destroyApp

Behavior of the destroyApp method

The destroyApp method begins by setting the kill flag belonging to the Worker thread to true. This tells the Worker thread to commit suicide the next time it checks the value of its kill flag.

Then the destroyApp method wakes the Worker thread up if it is asleep. This will cause it to check its kill flag and commit suicide fairly quickly.

Then the destroyApp method prints a message on the standard output device showing the relative time that it entered the destroyed state.

No reprieve requested
Note that in this example, the code in the destroyApp method doesn't exercise its right to request a reprieve from entering the destroyed state.

Finally, the destroyApp method calls the notifyDestroyed method to tell the AMS that the MIDlet is entering the destroyed state. Although I'm not certain, I assume that the AMS destroys its current reference to the MIDlet object at that time, causing that object, and all the objects on which it holds references to become eligible for garbage collection. In any event, after the destroyApp method is called, it becomes possible to click the top right soft key on the cell phone emulator keyboard shown in Figure 4 and launch the MIDlet again.

The resume method

The resume method is shown in its entirety in Listing 16.

Listing 16. The resume method.

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

Behavior of the resume method

The resume method is called by the Toggler thread to cause the MIDlet to call the resumeRequest method. This sends a request to the AMS to return the MIDlet from the paused state to the active state. If the request is honored, the AMS will call the startApp method on the MIDlet thread, leading to the behavior discussed earlier. (Note in particular that the Toggler thread does not call the startApp method directly but delegates that task back to the main MIDlet thread and the AMS.)

A member class
Not that it is terribly important, the Toggler class is also a member class of the class named LifeCycle01. I tend to use member classes, local classes, and anonymous classes frequently because it makes Java programming easier.

The beginning of the Toggler class

The beginning of the Thread class named Toggler is shown in Listing 17. It is important to note that this class extends the Thread class.

Listing 17. The beginning of the Toggler class.

  class Toggler extends Thread{
    LifeCycle01 theMIDlet;

    Toggler(LifeCycle01 theMIDlet){
      this.theMIDlet = theMIDlet;
    }//end constructor

Multithreaded programming
I am going to assume that you already understand multithreaded programming. If not, you may want to take a look here.

Purpose of the Toggler class

The purpose of an object of the Toggler class is to cause the main thread to toggle between the active state and the paused state every two seconds during a total of ten cycles. Then it causes the main thread to enter the destroyed state. In other words, the Toggler object exists solely to exercise the remaining code in the MIDlet to confirm that it is working properly.

A reference back to the MIDlet

The constructor for the Toggler object receives and saves a reference back to the MIDlet from which the thread was spawned. This makes it possible later to call methods on the MIDlet object.

The run method in a thread

Every Thread object has a method named run, and this is where the action begins and ends. I often tell my students that the run method in a thread is somewhat analogous to the main method in an application.

The run method is executed by calling the start method on the thread object. The run method can instantiate new objects, call other methods, etc., but when the run method terminates, the thread dies. That is to say, if you call the isAlive method on a thread before the run method terminates, it will return true. If you call the same method after the run method terminates, it will return false.

Making the threads eligible for garbage collection
When the AMS causes the MIDlet object to become eligible for garbage collection, this should in turn cause the thread objects held by the MIDlet object to also become eligible for garbage collection.

Not eligible for garbage collection yet

It is important to note, however that causing a thread to die does not make the Thread object eligible for garbage collection. The Thread object continues to occupy memory for as long as there is an active reference to the object being held by the MIDlet program. As a result, it is possible to call the start method on the object and bring the thread back to life. However, even though the MIDlet continues to hold a reference to the Toggler object, it isn't brought back to life in this MIDlet once it has died.

Beginning of the run method in the Toggler object

The run method in the Toggler object begins in Listing 18.

Listing 18. Beginning of the run method in the Toggler object.

    public void run(){
      for(int cnt = 0;cnt < 10;cnt++){
        //Sleep for two seconds.
        try{Thread.currentThread().sleep(2000);
        }catch(Exception e){}

The method begins by entering a for loop that it will iterate ten times. Then it immediately puts itself to sleep for two seconds (2000 milliseconds).

Wake up and do something

After two seconds, the Toggler thread wakes up naturally and executes the if-else construct shown in Listing 19.

Listing 19. Wake up and do something.

        if(cnt % 2 == 0){
          theMIDlet.pauseApp();
        }else{
          theMIDlet.resume();
        }//end else
      }//end for loop

If the counter value is even...

The conditional clause in the if statement tests to determine if the current value of the counter in the for loop is even or odd. If the counter value is even, the Toggler calls the pauseApp method on the MIDlet to cause the MIDlet to enter the paused state. Then control goes back to the top of the for loop where the Toggler goes back to sleep for two more seconds.

If the counter value is odd...

If the counter value is odd, the Toggler calls the resume method on the MIDlet to cause the MIDlet to send a request to the AMS to return it to the active state as explained earlier. Also as explained earlier, if the request is honored, by the AMS, the AMS will call the startApp method on the main MIDlet thread. Once again, that the Toggler object does not call the startApp method directly.

Then the Toggler object goes back to sleep for another two seconds.

Cause the MIDlet to enter the destroyed state

Listing 20 shows the code that is executed when the for loop counter reaches 10 and the conditional clause in Listing 18 is false.

Listing 20. Cause the MIDlet to enter the destroyed state.

      theMIDlet.destroyApp(true);
    }//end run
  }//end class Toggler

At that point, the Toggler calls the destroyApp method on the MIDlet causing the MIDlet to enter the destroyed state as described earlier.

The Toggler thread dies

At this point, the run method terminates causing the Toggler thread to die.

The Thread class named Worker

Listing 21 shows the beginning of the final Thread class that I will discuss in this MIDlet program.

Listing 21. The Thread class named Worker.

  class Worker extends Thread{
    boolean paused;//true indicates paused
    boolean kill;//true means time to die
    long shortSleep = 500;//one-half second
    long longSleep = 25000000;//6.94 hours
    long sleepTime = shortSleep;

    Worker(boolean paused,boolean kill){//constructor
      this.paused = paused;
      this.kill = kill;
    }//end constructor

The purpose of a Worker object

This class is also 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.

The work that gets done is to display some period characters on the standard output device. Although this isn't very sophisticated work, it is work nonetheless.

The Worker object also sleeps some of the time while the MIDlet is in the active state to avoid consuming computer resources.

Declare and initialize some instance variables

The Worker class begins by declaring and in some cases initializing some instance variables that are used as working variables. The initial values for two of those variables are received as incoming parameters to the constructor. The need for these variables should become clear once I begin explaining the code.

If you look back at Listing 11, you will see that the paused variable in Listing 21 is initialized to true by the constructor and the kill variable in Listing 21 is initialized to false by the constructor.

The beginning of the run method for the Worker class

Listing 22 shows the beginning of the run method for the class named Worker.

Listing 22. The beginning of the run method for the Worker class.

    public void run(){
      System.out.print("I'm working ");
      while(true){
        //Check the kill flag and behave appropriately.
        if(kill){
          System.out.println("Terminating worker");
          //This break will cause the thread to break out
          // of the while loop and to die.
          break;
        }//end if

The run method displays a message telling us that it is working. Then it enters an infinite loop.

Is it time to die?

Once inside the infinite loop, it immediately checks the value of the kill flag to see if it is time to die. If so, it displays a message to that effect and breaks out of the infinite while loop.

As you will see later, when control exits the while loop, the run method terminates and the Worker thread dies.

When the kill flag is false...

If the kill flag is false, the code in Listing 23 is executed.

Listing 23. When the kill flag is false.

        //Check the paused flag and behave appropriately.
        if(!paused){
          //Do some work and then go to sleep for a short
          // time period.  The thread will sleep until it
          // awakes normally or until it is interrupted.
          System.out.print(".");
          sleepTime = shortSleep;
        }else{
          //The main thread is in the paused state.  Go
          // to sleep for a very long time.
          sleepTime = longSleep;
        }//end else

Listing 23 checks the paused flag to see if the MIDlet is in the paused state. As mentioned above, the paused flag will be initialized to true when the Worker object is first constructed. It will first be changed to false when the startApp method is called by the AMS to put the MIDlet in the active state. (See Listing 12.)

When the paused flag is true...

When the paused flag is true, the else clause in Listing 23 is executed, setting the sleepTime variable to the value of longSleep. This will ultimately cause the Worker thread to go to sleep for a very long time. As a practical matter, it will continue to sleep until it is awakened by the startApp method calling the interrupt method on the Worker object telling it to wake up and do some work. (See Listing 13.)

When the paused flag is false...

When the paused flag is false, the if clause in Listing 23 will be executed. This causes the Worker object to display a period character on the standard output device, which is the work that this simple MIDlet is supposed to do.

Then the code in Listing 23 sets the sleepTime variable to the value of shortSleep. This will ultimately cause the Worker thread to go to sleep for a short time period before it wakes up and does some more work.

The Worker thread goes to sleep

The code in Listing 24 causes the Worker thread to go to sleep for a time period specified by the current value of sleepTime. As you can see in Listing 21, that period will be either one-half second, or 6.94 hours, depending on whether the value of shortSleep or the value of longSleep was assigned to sleepTime earlier.

Listing 24. The Worker thread goes to sleep.

        try{
          Thread.currentThread().sleep(sleepTime);
        }catch(InterruptedException e){
          System.out.print("I'm awake ");
        }//end catch

      }//end while loop

      //This thread will die at this point.
    }//end run
  }//end class Worker
}//end class LifeCycle01

Waking naturally

If the specified sleep time passes without another thread calling the interrupt method on the sleeping Worker thread, it will awake of its own accord. Control will transfer back to the top of the infinite while loop where it will test the values of the kill flag and the paused flag to decide what to do next. (See Listing 22 and Listing 23.)

Being awakened by another thread

If another thread calls the interrupt method on the sleeping Worker thread, it will throw an InterruptedException. This will cause control to be transferred unconditionally to the body of the catch block in Listing 24. In that case, it will display an "I'm awake" message. Then control will exit the catch block and go back to the top of the infinite while loop where it will test the values of the kill flag and the paused flag to decide what to do next.

Once again, see Listing 22 and Listing 23. As explained earlier, if the kill flag is true, control will break out of the infinite loop, the run method will terminate, and the Worker thread will die.

If both the kill flag and the paused flag are false, the Worker thread will display a single period character on the standard output device and go back to sleep for one-half second.

If the kill flag is false but the paused flag is true, the Worker thread will go to sleep for 6.94 hours. As a practical matter, it will probably sleep until it is awakened by another thread, but if that time passes and it wakes up naturally, it will check the kill flag and the paused flag and behave accordingly.

End of the run method, the Worker class, and the LifeCycle01 class

Listing 24 also signals the end of the run method, the Worker class, and the LifeCycle01 class. It also signals the end of the explanation of the MIDlet named LifeCycle01.

Run the program

I encourage you to copy the code from Listing 25 and Listing 26 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 (seeResources).

Experiment with the MIDlet code in conjunction with the framework program from the earlier lesson. Make some changes to the MIDlets and observe the results 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

I began by explaining how the CLDC and the MIDP fit into the grand scheme of things in MIDlet programming.

Then I provided a simple example to show you how to change the behavior of a Java program by replacing the classes in the Java standard library with custom classes of your own design.

Then I presented and explained a MIDlet named LifeCycle02 that demonstrates that the required behavior for each of the three states of a MIDlet is not enforced. The implication of the lack of enforcement is that it is the responsibility of the MIDlet programmer to make certain that the MIDlet follows the life-cycle rules.

Finally I presented and explained a MIDlet named LifeCycle01 that illustrates one approach to writing MIDlet code that does play by the rules.

What's next?

In the next lesson, you will learn:

  • The fundamentals of user interfaces for MIDlets.
  • How to instantiate user interface components.
  • How to cause user interface components 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.
  • About the methods of the various classes that can be used to manipulate user input and output.

Resources

Complete program listings

Complete listings of the life cycle programs discussed in this lesson are shown in Listing 25 and Listing 26 below. Listings of the shorter DateDemo programs are shown in Listing 2, Listing 3, and Listing 4.

Listing 25. The MIDlet named LifeCycle02.

/*LifeCycle02.java
Copyright 2007, R.G.Baldwin
December 9, 2007

This MIDlet demonstrates that a MIDlet can continue to
execute code while in the paused state or in the
destroyed state in the Sun cell phone emulator.

During the time that the MIDlet is in the paused state,
the Sun cell phone emulator displays a box with the
following words on the cell phone screen:

Incoming Call ...

In addition, a red indicator shows in the middle of the
right-most soft key while the MIDlet is in the paused
state. Despite this, however, the MIDlet continues to
execute code and display text on the standard output
screen.

The MIDlet also continues to execute code and to display
output on the standard output screen after it enters 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 LifeCycle02;

import javax.microedition.midlet.MIDlet;
import java.lang.Thread;
import java.util.Date;

public class LifeCycle02 extends MIDlet{

  long timeBase;
  boolean running = false;

  public LifeCycle02(){
    //Establish the time (in milliseconds relative to
    // Jan 1, 1970) that the MIDlet object is constructed.
    // This time will be used to compute the elapsed times
    // in milliseconds at which other events occur
    // relative to the construction time of the MIDlet.
    timeBase = new Date().getTime();
    System.out.println("Constructed at: "
                       + (new Date().getTime()-timeBase));
  }//end constructor

  //The following method is called by the AMS to change
  // the state of the MIDlet from paused to active.
  public void startApp(){

    System.out.println("startApp method called at: "
                       + (new Date().getTime()-timeBase));
    //Make the AMS believe that the MIDlet is paused
    pauseApp();
    //Now keep executing code.  Note that the Sun's cell
    // phone emulator displays an Incoming Call message on
    // its screen from this point until the MIDlet enters
    // the destroyed state.
    for(int cnt = 0;cnt < 5;cnt++){
      try{Thread.currentThread().sleep(1000);
      }catch(InterruptedException e){}
      System.out.println("Time is: "
                       + (new Date().getTime()-timeBase));
    }//end for loop

    //Destroy the MIDlet.
    destroyApp(true);
    
    //Try to execute some more code.
    for(int cnt = 0;cnt < 5;cnt++){
      try{Thread.currentThread().sleep(1000);
      }catch(InterruptedException e){}
      System.out.println("Time is: "
                       + (new Date().getTime()-timeBase));
    }//end for loop
    
  }//end startApp
  //----------------------------------------------------//

  public void pauseApp(){
    notifyPaused();
    System.out.println("\npauseApp method called at: "
                       + (new Date().getTime()-timeBase));
  }//end pauseApp
  //----------------------------------------------------//

  public void destroyApp(boolean unconditional){
    notifyDestroyed();
    System.out.println("destroyApp method called at:  "
                       + (new Date().getTime()-timeBase));
  }//end destroyApp
  //----------------------------------------------------//

}//end class LifeCycle02
//======================================================//

Listing 26. The MIDlet program named LifeCycle01.

/*LifeCycle01.java
Copyright 2007, R.G.Baldwin
December 9, 2007

The purpose of this MIDlet is to demonstrate the following
three states in which a MIDlet can reside:
paused
active
destroyed

This MIDlet is comprised of three threads.  One thread is
the main MIDlet thread.

A second thread is instantiated from a class named
Toggler. This thread is spawned and run to toggle the
MIDlet between the active and paused states at
approximately two-second intervals. After approximately
twenty seconds, the Toggler instructs the MIDlet to enter
the destroyed state.

A third thread is instantiated from a class named Worker.
This thread is spawned and run to do some work while the
MIDlet is in the active state.  The work that it does is
to display a period on the standard output device
approximately once every half second.  This thread sleeps
while the MIDlet is in the paused state, and dies when the
MIDlet enters the destroyed state. (It also sleeps some of
the time that the MIDlet is in the active state to avoid
consuming excessive computational resources.)

During those time intervals that the MIDlet is in the
paused state, the Sun cell phone emulator displays a
box with the following words on the cell phone screen:

Incoming Call ...

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

package LifeCycle01;

import javax.microedition.midlet.MIDlet;
import java.lang.Thread;
import java.util.Date;

public class LifeCycle01 extends MIDlet{

  Toggler theToggler;
  Worker theWorker;
  long timeBase;
  boolean running = false;

  public LifeCycle01(){
    theToggler = new Toggler(this);
    theWorker = new Worker(true,false);
    //Establish the time (in milliseconds relative to
    // Jan 1, 1970) that the MIDlet object is constructed.
    // This time will be used to compute the elapsed times
    // in milliseconds at which other events occur
    // relative to the construction time of the MIDlet.
    timeBase = new Date().getTime();
    System.out.println("Constructed at: "
                       + (new Date().getTime()-timeBase));
  }//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("Started at: "
                       + (new Date().getTime()-timeBase));
      running = true;
      //Start the Toggler thread running.
      theToggler.start();
      //Start the worker thread running in an active
      // (not-paused) MIDlet state.
      theWorker.paused = false;
      theWorker.start();
    }else{
      //This is not the first time that this method has
      // been called.
      System.out.println("Re-started at: "
                       + (new Date().getTime()-timeBase));
      //Wake the Worker thread up if it is asleep. Set
      // the paused flag to false to tell the worker to
      // get to work.
      theWorker.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(){
    System.out.println("\nPaused at: "
                       + (new Date().getTime()-timeBase));
    //Tell Worker to go to sleep for a long time the next
    // time it checks the paused flag.
    theWorker.paused = true;
    //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.
  public void destroyApp(boolean unconditional){
    //Tell the Worker to terminate the next time it checks
    // its flags.
    theWorker.kill = true;
    //Wake the worker up if it is asleep.
    theWorker.interrupt();
    System.out.println("Destroyed at:  "
                       + (new Date().getTime()-timeBase));
    //Tell the AMS that the MIDlet is in the destroyed
    // state. It can then be launched again.
    notifyDestroyed();
  }//end destroyApp

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

  //This is a member class.  The purpose of an object of
  // this class is to cause the main thread to toggle
  // between the active state and the paused state every
  // two seconds during a total of ten cycles.  Then it
  // causes the main thread to enter the destroyed state.
  class Toggler extends Thread{
    LifeCycle01 theMIDlet;

    Toggler(LifeCycle01 theMIDlet){
      this.theMIDlet = theMIDlet;
    }//end constructor

    public void run(){
      for(int cnt = 0;cnt < 10;cnt++){
        //Sleep for two seconds.
        try{Thread.currentThread().sleep(2000);
        }catch(Exception e){}

        if(cnt % 2 == 0){
          //Tell the main thread to enter the paused
          // state.
          theMIDlet.pauseApp();
        }else{
          //Cause the main 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
          // main thread. Note in particular that this
          // code does not call the startApp method
          // directly.
          theMIDlet.resume();
        }//end else
      }//end for loop

      //Instruct the main thread to enter the destroyed
      // state.
      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 (display periods)
  // while the main thread is in the active state and
  // to sleep while the main thread is in the paused
  // state.  It also sleeps some while the main thread
  // is in the active state to avoid consuming major
  // computational resources.
  class Worker extends Thread{
    boolean paused;//true indicates paused
    boolean kill;//true means time to die
    long shortSleep = 500;//one-half second
    long longSleep = 25000000;//6.94 hours
    long sleepTime = shortSleep;

    Worker(boolean paused,boolean kill){//constructor
      this.paused = paused;
      this.kill = kill;
    }//end constructor

    public void run(){
      System.out.print("I'm working ");
      while(true){
        //Check the kill flag and behave appropriately.
        if(kill){
          System.out.println("Terminating worker");
          //This break will cause the thread to break out
          // of the while loop and to die.
          break;
        }//end if

        //Check the paused flag and behave appropriately.
        if(!paused){
          //Do some work and then go to sleep for a short
          // time period.  The thread will sleep until it
          // awakes normally or until it is interrupted.
          System.out.print(".");
          sleepTime = shortSleep;
        }else{
          //The main thread is in the paused state.  Go
          // to sleep for a very long time.
          sleepTime = longSleep;
        }//end else

        try{
          Thread.currentThread().sleep(sleepTime);
        }catch(InterruptedException e){
          //Control reaches here when another thread 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.
          System.out.print("I'm awake ");
        }//end catch

      }//end while loop

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

}//end class LifeCycle01
//======================================================//


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