The
examples that we’re going to
demonstrate here, and throughout the rest of the book, are called
MIDlets. If you’ve programmed with Java applets or servlets
before, then you’ll likely recognize the similarities in the
“fill-in-the-method” program structure. This first
example, HelloMidlet.java
, shown in Example 1-1, creates a text box and then prints the
archetypal “Hello World” in a text box.
Example 1-1. “Hello World”
import javax.microedition.midlet.*; import javax.microedition.lcdui.*; public class HelloMidlet extends MIDlet { // The display for this MIDlet private Display display; // TextBox to display text TextBox box = null; public HelloMidlet( ) { } public void startApp( ) { display = Display.getDisplay(this); box = new TextBox("Simple Example", "Hello World", 20, 0); display.setCurrent(box); } /** * Pause is a no-op since there are no background activities or * record stores that need to be closed. */ public void pauseApp( ) { } /** * Destroy must cleanup everything not handled by the garbage * collector. In this case there is nothing to cleanup. */ public void destroyApp(boolean unconditional) { } }
This MIDlet consists of a public class definition that extends the
MIDlet
class found in
javax.microedition.midlet
. This superclass forms
the base of all MIDlets in J2ME. Our HelloMidlet
class contains a constructor, as well as the
startApp()
, pauseApp( )
, and destroyApp( )
methods that have been inherited from
the MIDlet
class. Note
that there is no main()
method in this program.
Instead, the startApp()
, pauseApp( )
, and destroyApp( )
methods are called
by the underlying framework to start up the MIDlet, to pause it, or
to destroy it.
Let’s start off by compiling our program on the command line. Using the command line is a bit more complex than the KToolbar application that comes with the Wireless Toolkit, so in order to simplify it, be sure that you have entered the additional environment variables shown above. However, there are several steps that we need to perform when compiling J2ME applications, and it’s important to see each of the steps as they occur.
As you would expect, the program must be saved in a file called
HelloMidlet.java
. However, before you compile it,
create a directory called tmpclasses. Then use
the following command to compile the MIDlet from the command line in
Windows:
C:\midlets> javac -g:none -d tmpclasses -bootclasspath %MIDPAPI% -classpath %J2MECLASSPATH% HelloMidlet.java
In Linux and Solaris, the command looks like the following:
>javac -g:none -d tmpclasses -bootclasspath $MIDPAPI -classpath $J2MECLASSPATH HelloMidlet.java
This command compiles the Java source file without any debugging
info, and sets the appropriate boot and J2ME classpaths to ensure
that we don’t pick up any J2SE classes. The end result of this
command is the creation of the HelloMidlet.class
file in the tmpclasses directory.
With the J2SE, a class file was all you needed to run the application. However, all MIDlet classes must be preverified before they can be run on a target device. Why is this necessary? Remember that one of the tasks of the standard Java virtual machine (the one that comes with the J2SE) is to perform bytecode verification . Bytecode verification is one of the most important steps of the Java security model. It performs such tasks as ensuring that the bytecodes of a Java class (and their operands) are all valid; that the code does not overflow or underflow the VM stack; that local variables are not used before they are initialized; that field, method, and class access control modifiers are respected, and other important tasks. However, most of the bytecode verifier is not included with the KVM due to size constraints. The preverifier ensures that the equivalent security checks still take place.
Before you run the preverifier, create another directory called
classes. Then, use this command to preverify the
HelloMidlet
class:
C:\midlets> preverify -classpath %MIDPAPI%;tmpclasses -d classes tmpclasses
Or on Solaris and Linux:
> preverify -classpath $MIDPAPI:tmpclasses -d classes tmpclasses
The resulting output should look something like this:
[Output directory for verified classes: classes]
This command takes all the classes inside the
tmpclasses directory (of which
HelloMidlet.class
is the only one) and preverifies
them, writing the resulting classes to the
classes directory. Note that the names of the
preverified classes remain exactly the same, which is why we created
two separate directories to hold them.
Tip
If you received an “Illegal constant pool index” class loading error and you’re using JDK 1.4, try using JDK 1.3 until this issue is resolved.
The next step is to compress all the
classes in the program (again, we
have only one) as well as their resources, into a
Java Archive
(JAR) file. You can use the J2SE
jar
command to create a
JAR
file. Make sure you are in the classes directory
to execute the following command:
> jar cvf HelloMidlet.jar HelloMidlet.class
The program will compress the HelloMidlet
class
into a JAR file, creating a manifest for it as well.
Note that with the javac
compiler, you can create
MIDlets of practically any size. However, that doesn’t
guarantee that they will fit on the target device for which
you’re writing the MIDlet. It would nice if there were a way to
check if the target device can handle the MIDlet and run it before it
is downloaded. Obviously, if a device can’t handle the MIDlet,
there is no reason to even attempt a download.
To accomplish this, we need a file that manually specifies some pre-download properties, including the size of the MIDlet and its storage requirements. This can be accomplished by creating a Java Application Descriptor (JAD) file with your favorite text editor. Example 1-2 shows a sample JAD file that we can use. Note that you will need to change the MIDlet-Jar-Size entry to correspond to the size of the JAR file that you just created. (In Chapter 3, we will explain the JAD file syntax in more detail.)
Example 1-2. HelloMidlet.jad
MIDlet-1: Hello,,HelloMidlet MIDlet-Name: HelloMidlet MIDlet-Version: 1.0 MIDlet-Vendor: ORA MIDlet-Jar-URL: HelloMidlet.jar MIDlet-Jar-Size: 863
Let’s save this example JAD file as
HelloMidlet.jad
, again in the classes
directory that holds the JAR file. Finally, to run this MIDlet,
invoke Sun’s MIDP
emulator to point at the JAD file
using the following command:
> emulator -Xdescriptor:HelloMidlet.jad
If everything worked correctly, you should see a phone similar to Figure 1-4, although the display may be different. Here, the HelloMidlet is running in the default phone that comes with the Java Wireless Toolkit. If you click on the MIDlet on the menu (use the directional arrow pad to move the cursor and the button in the middle to select), and instruct it to “Launch” using the soft button on the lower right, you should see output similar to Figure 1-4. Congratulations! You just created your first Java MIDlet!
The gist of this program is in the startApp( )
method. Here, we obtain the current
display that the device uses, then create a text box with the words
“Hello World” inside of it. Finally, we show the text box
on the current display. Don’t worry if you don’t
understand these objects yet; the architecture of MIDlets will become
clearer as we move through the book.
Let’s move to a more advanced MIDlet. Example 1-3 shows a MIDlet with a hypothetical login screen that prompts the user to log in. If the login is incorrect, the program will repeatedly ask the user to try again.
Example 1-3. A login MIDlet
import javax.microedition.midlet.MIDlet; import javax.microedition.lcdui.*; public class LoginMidlet extends MIDlet implements CommandListener { private Display display; private TextField userName; private TextField password; private Form form; private Command cancel; private Command login; public LoginMidlet( ) { userName = new TextField("LoginID:", "", 10, TextField.ANY); password = new TextField("Password:", "", 10, TextField.PASSWORD); form = new Form("Sign in"); cancel = new Command("Cancel", Command.CANCEL, 2); login = new Command("Login", Command.OK, 2); } public void startApp( ) { display = Display.getDisplay(this); form.append(userName); form.append(password); form.addCommand(cancel); form.addCommand(login); form.setCommandListener(this); display.setCurrent(form); } public void pauseApp( ) { } public void destroyApp(boolean unconditional) { notifyDestroyed( ); } public void validateUser(String name, String password) { if (name.equals("qm") && password.equals("j2")) { menu( ); } else { tryAgain( ); } } public void menu( ) { List services = new List("Choose one", Choice.EXCLUSIVE); services.append("Check Mail", null); services.append("Compose", null); services.append("Addresses", null); services.append("Options", null); services.append("Sign Out", null); display.setCurrent(services); } public void tryAgain( ) { Alert error = new Alert("Login Incorrect", "Please try again", null, AlertType.ERROR); error.setTimeout(Alert.FOREVER); userName.setString(""); password.setString(""); display.setCurrent(error, form); } public void commandAction(Command c, Displayable d) { String label = c.getLabel( ); if(label.equals("Cancel")) { destroyApp(true); } else if(label.equals("Login")) { validateUser(userName.getString(), password.getString( )); } } }
Again, don’t worry if you can’t understand the entire
program at this point; this example is just meant to give you a
flavor of MIDP programming and some sample applications to compile
and run. Chapter 5 and Chapter 6 will explain the GUI classes (such as
Display
, Form
, and
TextField
), as well as the event-handling classes
(such as Command
) in much more detail.
That being said, let’s present a beginner’s overview of
how this MIDlet works. As in the previous example,
LoginMidlet
extends the MIDlet
abstract class. It also implements the
CommandListener
interface by providing an implementation for the
commandAction( )
method. In this method, there are
two commands: Login
and Cancel
.
The label of the command is checked: if it is
Cancel
, the LoginMidlet is destroyed, and if it is
Login
, then the username and passwords are
validated.
In the LoginMidlet’s constructor, a Form
object, two TextField
objects, and two
Command
objects are created. The
TextField
and Command
objects
are added to the form in the
startApp()
method. In addition,
pauseApp()
and destroyApp( )
perform minimal tasks.
Here is how the program operates: if the Login
command is given, the application calls the validateUser( )
method to validate the username and password. If they are valid (in
this case, they are hardcoded into the program for simplicity), then
the menu( )
method
is called to simulate a list of “useful services.”
Otherwise, the tryAgain( )
is
called to display an error message and to allow the user to reenter
their name and password.
If you are using the command line to compile and execute, save this
file named LoginMidlet.java
, make sure that you
have a classes and a
tmpclasses directory, and use
javac
:
C:\midlets> javac -g:none -d tmpclasses -bootclasspath %MIDPAPI% -classpath %J2MECLASSPATH% LoginMidlet.java
If you are using Solaris or Linux, the command becomes:
>javac -g:none -d tmpclasses -bootclasspath $MIDPAPI -classpath $J2MECLASSPATH LoginMidlet.java
Next, remember that we must preverify the resulting class:
C:\midlets> preverify -classpath %MIDPAPI%;tmpclasses -d classes tmpclasses
or
> preverify -classpath $MIDPAPI:tmpclasses -d classes tmpclasses
Again, the preverified class is saved to the classes subdirectory in the current directory. Next, compress the resulting class into a JAR file:
jar cvf LoginMidlet.jar LoginMidlet.class
And finally, create a
JAD file that describes the resulting JAR
file in detail, as shown in Example 1-4.
Example 1-4. LoginMidlet.jad
MIDlet-1: Login,,LoginMidlet MIDlet-Name: LoginMidlet MIDlet-Version: 1.0 MIDlet-Vendor: ORA MIDlet-Jar-URL: LoginMidlet.jar MIDlet-Jar-Size: 1786
Again, don’t forget to change the size of the JAR file to match
the size of the
LoginMidlet.jar
file after you create it.
At this point, the MIDlet can be run as in the previous example, using the MIDP emulator of the Java Wireless Toolkit, with the following command:
emulator -Xdescriptor:LoginMidlet.jad
In addition, the MIDlet can be run with any other emulator you may have available. For example, to whet your appetite, Figure 1-5 shows the LoginMidlet running on the Motorola i85s emulator (the i85s is a J2ME-enabled cell phone available from Motorola and Nextel).
Note that the objects represented by the Command
class are shown above the two “soft buttons” on the phone
(the buttons with the black circles). If a soft button below the
command is pressed, the command immediately above it is executed.
Here, if the user enters the correct username and matching password
and presses the Login button, the menu of services will be displayed.
Otherwise, the alert will be displayed and the user can try again.
Also, you might be caught off guard the first time you try to enter text with your computer keyboard. It doesn’t work! That’s because you must use the input keys on the phone to enter the text. In this case, to enter the letter “G”, press the number “4.” To enter the letter “K”, press the number “5” twice. Note how each time you press a numeral, the system “cycles” through the letter corresponding to that number. To move down to entering text for the password, use the down arrow.
Well, that’s it! You’ve just created two professional MIDlets using J2ME! In the next two chapters, we’re going to take a much closer look at the CLDC and the MIDP, two exciting new areas of wireless Java development.
Get Wireless Java now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.