In this article I discuss which displayable components are available
in J2ME and explain how commands are associated with them. In
particular, I examine how to create commands, add them to
displayables, and define command behavior in CommandListeners. I then
demonstrate how the resulting code is typically procedural and
quickly becomes cluttered when too many commands are created.
Using object-oriented principles and the Command pattern, I
present two new classes that provide a simple solution to this
problem. These classes provide structure and reusability to the
process of creating commands for displayables, resulting in code
that's cleaner and easier to maintain.
Overview of Displayable Components
In the Mobile Information Device Profile (MIDP) there's no
AWT (and certainly no Swing) for creating graphical user interfaces.
Instead, GUIs are created using classes from the
javax.microedition.lcdui package. As shown in Figure 1, the abstract
Displayable class is the superclass from which you create objects
that can be displayed on the screen of a MIDP device.
The two immediate subclasses of Displayable are Canvas and
Screen. The Canvas class creates GUI components that use low-level
graphical primitives to draw themselves. To use the Canvas class you
must subclass it and implement its paint() method, which receives a
Graphics parameter. The Graphics class, similar to its J2SE
counterpart, provides methods to draw lines, rectangles, arcs,
strings, and images.
The Screen class, on the other hand, creates high-level GUI
components. Like the Canvas class it's abstract, but it has four
direct subclasses that are concrete and ready to use: Alert, List,
TextBox, and Form. All but the Form, which is quite blank on its own,
are shown in Figure 2. The Alert class displays a message similar to
a dialog box. The List presents choices using one of the following
schemes: EXCLUSIVE, MULTIPLE, or IMPLICIT. The TextBox allows the
user to enter text and can optionally restrict the input format; for
example, permit numbers only.
The Form class, unlike its three siblings, is not a specific
GUI component; rather it serves as a container for the other
components, making it possible to build more complex user interfaces.
The components that the Form contains are subclasses of the Item
class. These include the ChoiceGroup (equivalent to the List),
DateField, Gauge, ImageItem, StringItem, and TextField. There are no
layout managers in MIDP; instead, the device automatically handles
the layout, traversal, and scrolling of the items that are added to a
Form. In most cases, the components are laid out vertically.
Besides having graphical representation, displayable
components can also have additional behavior associated with them. To
do this, add commands and set a CommandListener that will be notified
when the commands are invoked.
Commands and CommandListeners
Figure 3 shows the relationship between the Displayable
class, commands, and CommandListeners. Notice that multiple commands
may be added to a displayable, but only one CommandListener may be
set. I'll examine some of the consequences of this later. For now,
let's look at how to create commands and Command-Listeners.
To create a command, specify the label string, type, and
priority of the command. The label string represents the command in
the user interface. The command type gives a hint to the device on
the semantics of the command. It's up to the device implementation to
decide which scheme to use for commands of a certain type. The
defined command types are BACK, CANCEL, EXIT, HELP, ITEM, OK, SCREEN,
and STOP. And the priority is an integer that indicates the
importance of the command with respect to other commands. Commands
with a smaller number are given a higher priority. When adding more
commands than the maximum that the device can display at one time,
commands with a higher priority will be shown and those of a lower
priority will be available only by other means (a menu, for example).
After creating a command, add it to the displayable by using its addCommand() method. For example:
Command command = new Command("Go",
Command.SCREEN, 0);
displayable.addCommand(command);
The Command class is not where you define what happens when
the command is invoked. Instead, you must create a class that
implements the CommandListener interface, which contains one method:
public void commandAction(Command c,Displayable d);
This method is called when a command that was added to a
displayable is invoked, so this is where you place the behavior for
your commands. After creating your CommandListener, associate it with
the displayable by using the setCommandListener() method. Notice that
the method is not addCommandListener() as would have been the norm in
J2SE. As stated earlier, in J2ME there can be only one
CommandListener per displayable. This means that the behavior for
every command of a displayable must be defined in one implementation
of CommandListener.
Procedural Example
I'll use an example to illustrate the use and consequences of
the current Command->CommandListener process. Consider a simple
MIDlet that displays the three different types of Lists: EXCLUSIVE,
MULTIPLE, and IMPLICIT. The MIDlet displays an empty Form with four
commands: one for each type of List, and an Exit command.
When the user selects a type, a List of that type with
arbitrary choices is displayed. The user interacts with the List and
can select OK or Cancel. OK displays a confirmation of the selected
List item(s), while Cancel goes back to the Form that offers the List
type choices.
Listing 1 shows the code for this example. In line 6, I
create the initial Form. Lines 8-11 define the items that will be
displayed in each List and a Boolean array that will contain flags
indicating the selected items. Lines 13-20 create the four commands
for the Form.
Lines 22-25 define the OK and the Cancel commands for the Lists.
In the startApp() method, I add the four commands to the Form
and set its CommandListener, defined on lines 44-69. In this code,
note that I determine the List type according to the invoked command.
I then create a List of that type and add the OK and Cancel commands.
Finally, I set the CommandListener, defined in lines 71-100, and
display the List.
Note that line 79 tests for the OK command as well as the
special List.SELECT_COMMAND; this command is invoked when the user
makes a selection on a List of type IMPLICIT, so we must test for it.
Listing 1 demonstrates the process of adding commands to
displayables and associated Comm-
andListeners to define the behavior for the commands. However,
defining the behavior separate from the command, combined with the
restriction of having only one listener per displayable, yields
procedural code that prevents us from leveraging OO principles.
Resulting problems include:
- The CommandListener's commandAction() method quickly becomes monolithic and littered with if/else statements.
- If you dynamically remove a command from a displayable, the
corresponding conditional statement in the commandAction() method
remains and will needlessly be tested when other commands are invoked.
- The CommandListener is tightly coupled with the commands that
it services. To add another command to a displayable, you must not
only add the code for the command, but also modify the
CommandListener.
- The command and its behavior are defined in two separate
places in the code, hindering readability.
- You can't create objects that define the behavior for your
commands. So you can't take advantage of features that OO provides,
like inheritance and polymorphism.
After creating a few MIDlets, I became frustrated by these
problems in the command creation process. There had to be a better
way.
Command Pattern and CommandAction
Creating commands and their behavior the OO way and solving
the problems enumerated in the previous section meant opening my copy
of Design Patterns (see Resources section) and reviewing the Command
pattern. Its intent is to encapsulate a request as an object by
providing a method that will be invoked to execute the command.
Refactoring (see Resources) confirms this by identifying a series of
if/else statements (or switch statements) as a "bad smell" in the
code that should be refactored. The proposed solution is to "Replace
Conditional with Polymorphism": move each leg of the conditional to
an overriding method in a subclass and make the original method
abstract.
To do this, I created the CommandAction class, which contains
a command and provides the aforementioned method:
public abstract void execute(Displayable d);
This method makes it possible to associate the behavior of
the command with the command itself. For convenience, the
CommandAction class (see Listing 2) provides two constructors, one
that accepts a command and one that accepts the label, type, and
priority and creates a command from those parameters. Now, the
command and its behavior are encapsulated in an object. For example:
private CommandAction goCommand
= new CommandAction("Go", Command.SCREEN, 0)
{
public void execute(Displayable d) {
// behavior code for the Go command
}
};
The CommandManager Class
The second class I needed was one that accepts
CommandActions, adds the commands they contain to displayables, and
invokes their execute() method when the command is invoked. I called
this class CommandManager. It provides a method to add a
CommandAction to a displayable:
public void add(CommandAction cmdAction,
Displayable displayable);
In your MIDlet, create a single CommandManager that will
handle all CommandActions for all displayables. A remove method is
also provided:
public void remove(CommandAction cmdAction,
Displayable d);
In the CommandManager class, I use three Hashtables, one to
answer each of the following questions:
- Hashtable 1: Given a displayable, how many commands have been
added to it?
- Hashtable 2: Given a command, what is its corresponding CommandAction?
- Hashtable 3: Given a CommandAction, to how many displayables has it been added?
When a CommandAction is added to a displayable, the
CommandManager adds the command contained by the CommandAction to the
displayable and increments the counter for that displayable in
Hashtable 1. If this counter, after being incremented, is 1, the
CommandManager registers itself as the CommandListener for this
displayable. If the counter is greater than 1, the CommandListener is
already registered.
Similarily, every time a CommandAction is removed from a
displayable, the CommandManager removes the corresponding command from the displayable and decrements the counter. If this counter goes back to 0, the CommandManager removes itself as a CommandListener for that displayable by calling
setCommandListener(null). The entry is also removed from Hashtable 1.
Hashtable 2 is necessary because the commandAction() method
receives a command parameter and we must determine the corresponding
CommandAction in order to call its execute() method.
Hashtable 3 tells us whether upon removing a command, we can
remove the Command->CommandAction mapping from Hashtable 2. Since
it's possible that the same command is used for more than one
displayable, we must make sure that no displayables use the command
before removing the entry from Hashtable 2.
The complete code for the CommandManager class is shown in
Listing 3 . Note: The Hashtables use 1-element int arrays for counters
because they require object values; when using integer objects you
have to create new integers each time, which results in a lot of
garbage to be collected.
Object-Oriented Example
Let's now implement the previous example using the
CommandAction and CommandManager classes (see Listing 4).
Line 7 creates the CommandManager that will be used for all
commands of all displayables.
Now, instead of having the if/else statements to determine
which type of List the user chose, we can define a class that extends
CommandAction and constructs a command and its behavior based on the
List type (lines 14-27).
Lines 29-34 define the Exit command.
Remember that the OK command can be directly invoked by the
user, but the special List.SELECT_COMMAND can also be invoked when
the List type is IMPLICIT. Lines 36-60 define the OK command as a
subclass of CommandAction because we have to create two types, one
for the OK command and one for the List.SELECT_COMMAND. For
convenience, I provided both of the CommandAction constructors.
Lines 62-67 define the Cancel command.
Lines 70-73 show how easy it is to add the List
CommandActions to the Form using the CommandManager. You don't have
to bother with CommandListeners; the command behavior is defined in
the CommandAction. Similarily, lines 20-22 add the OK and Cancel
CommandActions to the List.
Benefits
I find that using the CommandAction and CommandManager classes yield benefits at all stages of coding: creation, modification, and maintenance. These benefits are the results of making the process more object-oriented. The key is to encapsulate a
command and its behavior in a single class. Having the two close
together in code also improves readability.
The CommandManager is a helper class that defines once and
for all the mechanism of adding and removing commands to displayables
and setting the CommandListener. This reuse reduces clutter in client
code.
OO principles can now be put to use when creating commands.
Since commands and CommandListeners are loosely coupled, adding or removing commands does not require changes to a CommandListener. Your code becomes more modular, and you can add new commands to your system without having to modify or recompile existing classes.
Conclusion
J2ME is an exciting platform for writing applications that
run on wireless devices. After writing several MIDlets, I realized
that there was a better way to create commands and CommandListeners.
Since these are used in just about every MIDlet, I felt it was
worthwhile to carefully consider the issue.
When a situation like this arises, add some utility classes
to help you program using the design you find most advantageous. Take
the time to do this once and for all; you'll be glad you did when
you're using those classes over and over again.
I hope I've encouraged you to strive for object-orientation
in general and to continue exploring J2ME in particular.
Resources
Gamma, E., Helm, R., Johnson, R., and Vlissides, J. (1995).
Design Patterns: Elements of Reusable Object-Oriented Software.
Addison-Wesley.
Fowler, M. (2000). Refactoring: Improving the Design of
Existing Code. Addison-Wesley.
Knudsen, J. (2001). Wireless Java: Developing with Java 2,
Micro Edition. Apress.
Java 2 Platform, Micro Edition: http://java.sun.com/products/j2me
Mobile Information Device Profile: http://java.sun.com/products/midp
Java 2 Platform Micro Edition, Wireless Toolkit: http://java.sun.com/products/j2mewtoolkit