|
|
|
|
In a previous article, Java 2 Micro Edition - Java for Small and Embedded Systems, we explored how Java 2 Micro Edition (J2ME) can be used to program Palm devices. A lot has changed on the J2ME landscape since then. In this article, I will give you an update on the current state of J2ME. We will then explore how J2ME can be used to extend your enterprise applications to wireless devices.
The Mobile Information Device Profile (MIDP) is the first J2ME profile to be available on multiple devices. Currently, MIDP is supported on three Nextel phones, on PalmOS compatible devices and RIM Blackberry devices. During 2002, we will see other mobile phone networks support MIDP including Sprint who recently announced support by mid-2002. One obvious omission is the current lack of MIDP support on PocketPC. However, PersonalJava implementations are available which have more capabilities than MIDP and the J9 VM from IBM should soon support MIDP. (In fact, if you are willing to replace the operating system, a full J2SE implementation is available from SavaJe.)
MIDP provides four major additions to the Connected Limited Device Configuration (CLDC), which was discussed in the previous article: a well-defined life-cycle for the application, a GUI toolkit, persistent storage and HTTP networking capabilities.
The entry point to any MIDP application is the MIDlet
class defined in the javax.microedition.midlet package.
Just as the Applet class contains methods that allow
the browser to control the lifecycle of an applet, the
MIDlet contains similar methods, which are manipulated
by a device-dependent application manager. However, unlike an
applet, a MIDlet is not a GUI component.
The MIDlet class defines the methods
startApp(), pauseApp() and
destroyApp(), which can be called either by the MIDlet
itself or by an application manager on the device. The application
manager is responsible for creating the MIDlet and starting it. It
will also pause the MIDlet, e.g. when a phone call comes in, and
will destroy the MIDlet if there is an uncaught exception.
(See Fig. 1)
Fig. 1 MIDlet Lifecycle
Here is a simple HelloMIDlet that shows how to use these methods.
import javax.microedition.midlet.*;
public class HelloMIDlet extends MIDlet {
public void startApp() {
System.out.println("Hello MIDlet");
}
public void pauseApp() {}
public void destroyApp(boolean unconditional) {}
}
Since the MIDlet class is not a GUI component, the
above code will not have any screen presence. Depending on the
device, you may not even see "Hello MIDlet" printed out. On most
emulators, standard out is redirected to a console window.
Each time the user invokes the MIDlet, "Hello MIDlet"
will be sent to standard out. Since we don't have to do anything
special when the MIDlet is paused or destroyed, these
methods are empty. Typically, pauseApp() and
destroyApp() would contain any code needed to free
resources that were grabbed by startApp(). It is
important to remember that startApp() can be called
multiple times during the lifetime of the MIDlet. If
you need to perform a task only when the application is first
started, for example showing a splash screen, it will be necessary
to set an appropriate flag in the startApp() method.
Building a GUI in MIDP is very different from the Java 2 Standard Edition (J2SE). In J2SE, we have a rich set of visual components, which we can place in our application using layout managers. In MIDP, the assumption is that the screen is not large enough to display a lot of components at once. Also, it is desirable to display the application in a way that is natural for the device it is running on. Just as AWT applications look different on different computer systems, MIDP applications may look different on different devices.
MIDP specifies both high-level and low-level user interface APIs in
the javax.microedition.lcdui package. The high level
API specifies a small set of Screen objects, only one of which can
be shown at a time. Because of the limited size of the devices, we
leave behind the notion of layout managers. When the object is
shown, it takes up all of the screen real estate available to MIDP.
(Note that some of the screen may be reserved for device-specific
information.) These objects include an Alert,
List, TextBox and Form. See
the table below for a description of each.
| Screen | Description | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Alert | Shows non-editable text and an image for a fixed period of time or until the user dismisses it. Alerts are comparable to modal dialogs in J2SE. | ||||||||||||||
| Form |
Displays a number of items at the same time. If all items cannot be fit on the screen at once, a scrolling mechanism is provided.
|
||||||||||||||
| List | Allows selection from a sequence of strings. Images can also be displayed with the text, but this capability is not guaranteed on all devices. | ||||||||||||||
| TextBox | Displays editable text |
We can now improve our HelloWorld MIDlet so that it
displays "Hello World" on the device's screen.
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
public class HelloMIDlet extends MIDlet implements CommandListener {
private Command exitCommand;
private Display display;
public HelloMIDlet() {
this.exitCommand = new Command("Exit", Command.SCREEN, 2);
}
public void startApp() {
this.display = Display.getDisplay(this);
TextBox textBox = new TextBox("Hello MIDlet", "Hello World!", 256,
TextField.ANY);
textBox.addCommand(this.exitCommand);
textBox.setCommandListener(this);
this.display.setCurrent(textBox);
}
public void pauseApp() {}
public void destroyApp(boolean unconditional) {}
public void commandAction(Command command, Displayable displayable) {
if (command == this.exitCommand) {
destroyApp(false);
notifyDestroyed();
}
}
}
Note that if a phone call is received while HelloMIDlet
is running, the MIDlet is paused. When the user resumes
HelloMIDlet, startApp() is called again
and the GUI is recreated from scratch. In a more sophisticated
application, you may instead test if display is
null to determine if the GUI needs to be built.
The HelloMIDlet class above is built using the
high-level GUI components. It also uses a Command to
indicate that the user has the option to exit the application.
Commands can be associated with any Screen component
except an Alert. (Unfortunately, adding a
Command to an Alert will produce a runtime
error, not a compiler error.) The same command can be associated
with multiple screens. Depending on the type of command (as defined
by constants in the Command class), the command may be
mapped to a hardware key or a soft key on the device. For example,
an exit command may be mapped to the "hang up" key on a phone. It is
up to the device manufacturers to map the commands appropriately.
MIDP gives no direct control to the developer for the placement of
the commands. Indirectly, we can affect the placement by
manipulating the command's type and priority.
In addition to the high-level API, the lcdui package
also defines a Canvas class, which provides similar
drawing functionality to the AWT Canvas. The
Canvas class also provides methods to access pointer
and keyboard events. This allows the developer to create interactive
games or custom components. However, cross-device compatibility may
be compromised. For example, a game, which relies on pointer-based
events, may not work on a phone. On the other hand, a game, which
only uses key events, will be very awkward (but possible) to use on
a Palm device. Thankfully, Canvas gives us methods like
hasPointerEvents() so that we can write code that works
in either environment.
MIDP provides the ability to persistently store data on the device.
The javax.microedition.rms package defines classes to
support data persistence across different invocations of the
MIDlet and across device power outs.
Only one concrete class, RecordStore, is defined. It
has static methods to create and destroy data stores as well as
instance methods to manipulate the contents of a record store. A
single record is stored as an array of bytes.
Having persistent data on the device itself allows network-dependent MIDP applications to run even when the network is inaccessible. For example, an application could store data on the device when no network is available and then send that data to a server at a later time. A full exploration of the capabilities of the MIDP persistent storage mechanism will be the subject of a future JNB article.
The CLDC defines a Connector framework to support networking. The framework is much smaller than the J2SE network support classes. However, it is very flexible and easy to use. The CLDC itself does not require support for any specific protocols. MIDP requires that HTTP support is available.
Creating an HTTP connection on a MIDP device is quite easy. The following code opens an HTTP connection, sends a GET request and reads some text from the response.
/**
* Use an HTTP GET request to retrieve the contents of the URL as a string.
* @param url location of the desired information
* @return the contents of the URL.
*/
public static String get(String url) throws IOException {
HttpConnection httpConnection = null;
InputStream inputStream = null;
String content;
try {
httpConnection = (HttpConnection) Connector.open(url);
httpConnection.setRequestProperty("User-Agent",
"Profile/MIDP-1.0 Configuration/CLDC-1.0");
httpConnection.setRequestProperty("Content-Language", "en-US");
// Check that everything is OK
int status = httpConnection.getResponseCode();
if (status != HttpConnection.HTTP_OK) {
// we could choose to handle redirects here by looking
// for a status of HttpConnection.HTTP_TEMP_REDIRECT,
// HttpConnection.HTTP_MOVED_TEMP, or
// HttpConnection.HTTP_MOVED_PERM
throw new IOException("Server response not OK");
}
// Get the response back from the server
inputStream = httpConnection.openInputStream();
String type = httpConnection.getType(); // Get the ContentType
int length = (int) httpConnection.getLength();
if (length > 0) {
byte[] responseData = new byte[length];
int actual = inputStream.read(responseData);
content = new String(responseData);
} else {
StringBuffer stringBuffer = new StringBuffer();
int ch = inputStream.read();
while (ch != -1) {
stringBuffer.append((char) ch);
ch = inputStream.read();
}
content = stringBuffer.toString();
}
} finally {
if (inputStream != null) {
inputStream.close();
}
if (httpConnection != null) {
httpConnection.close();
}
}
return content;
}
One of the most exciting aspects of J2ME is that it enables us to extend our network infrastructure into the wireless world. Using the networking classes described above, we can write software for wireless devices that interact with a server.
In order to demonstrate how to wirelessly enable an enterprise application, we will create a small client-server system that consists of a web browser client, a J2ME MIDP client and a servlet that can serve custom content to the two clients.
As with any job, it is important to have the right tools. There are several tools available on the market today for J2ME development. Sun has the J2ME Wireless Toolkit, which can run in stand-alone mode or as an integrated piece of Forte for Java. JBuilder 5 includes integration with the Nokia toolkit. Motorola and Nokia offer tools through their developer web sites designed to work with their phones. See the References section below for links to these sites.
We will write a simple servlet that simply echoes the data sent to it by the client. (See the previous JNB article What is a Servlet?).
The servlet will accept incoming requests, read the parameters of the request and then send back the parameter names and values as the response. The servlet will service both a standard HTML web browser client and a MIDP client. It will send HTML to the web browser and plain text to the MIDP client.
The code for the servlet, named EchoServlet can be
found here. You can access the
servlet directly by typing its URL into your browser and appending
some request parameters, e.g.
http://www.ociweb.com/J2ME/echo?myParam=Test+Param
. In the URL above, "myParam" is the parameter name and
"Test+Param" is the parameter value, where the "+" is used to
represent a space between the two words.
Now we will create an HTML front end to our application. Don't get too excited, though. We will create a simple web page that lets us test our Servlet. You can view the page at http://www.ociweb.com/J2ME. Simply type in a parameter value and click the GET or POST button. This will send the text to the servlet in a GET or POST request, respectively.
Now that we have our servlet working and we've tested it with our
web browser, we can begin the much more interesting task of creating
a MIDP client. The client consists of the
WirelessMIDlet class and the
HTTPHelper class. The
MIDlet builds the user interface.
HTTPHelper contains utility functions to send HTTP GET
and POST requests to a given URL and retrieve the responses as text.
The application itself consists of four screens. (See the images
below.) Initially, a splash screen is shown. Next, the user can type
in the URL for the servlet. For convenience, the URL of our servlet
is the default value. Next, the user can enter in two name/value
pairs and choose to either use GET or POST to send the data. Once
the servlet sends back a response, it is displayed in an
Alert.
|
|
|
|
|
We have explored how J2ME MIDP can break the tether of the wired
connection, gaining us access to enterprise functionality in the
wireless world. Using the simple and well-understood HTTP protocol,
MIDlets can access data anywhere on the Internet.
It is important to remember that, although we have access to all of the information on the Internet, MIDP applications should not be modeled after the familiar web browser paradigm to which we have all become accustomed. Because of the limited screen size, awkward input mechanisms, low computing power and slow connection speeds of these wireless devices, the browsing paradigm of the Web does not work. Instead, MIDP applications must be small targeted applications that solve a single problem quickly and easily with a minimum of user involvement.
Object Computing, Inc (OCI) has been providing educational services to clients, industries and universities since 1993. We offer one of the most comprehensive distributed Object Oriented training curricula in the country. These curricula focus on the fundamentals of OO technology; with close to 40 workshops in OOAD, Java, XML, C++/CORBA and Unix/Linux.
For further information regarding OCI's Educational Services programs, please visit our Educational Services section on the web or contact us at training.
|
|
|