This article is an introduction of MoMELib J2ME library and sharing of some my ideas about simplifying of development of business (not games) J2ME applications.
Under term “business applications” i mean command driven applications as opposite to control driven applications (i.e. games). Game mostly consists of one or a few threads that are functioning non-stop and periodically query input events, and change behavior based on these events. Business application mostly is in the ready state, waiting for a commands and after arrival executes them.
In my humble option, J2ME is good suited for development of games but not business applications. Commands are executed (according to MIDP specification) in AMS (Application Management Software) callback thread. In this case implementations of processing of commands should be as short as possible (not to block other callbacks) and more important thing - there is a great possibility of dead locks with AMS. The second case makes impossible to use serious code in commands processing.
MoMELib is a J2ME library, that tries to simplify development of business J2ME applications and provides even more capabilities for commands processing. This library introduces “commands execution thread”. A thread for processing command events. This thread is separate from AMS callback thread. In this case there is no need to worry about deadlocks with AMS and timing of callbacks implementations (deadlocks with other parts of application are of course still possible). Command events are processed serially. A command events queue is maintained, so commands can be activated while another command is processed. A term “command” is abstracted to an Object. Command here means any object not necessarily LCDUI Command (including it of course). This simplifies definition and introduces other usages of commands. It is easy to define a command. For example:
private static final Object NEXT_LINE = new Object(); // or private static final String PREV_LINE = "MoveToPrevLine";
Recall commands are tested based on identity not equality. It is also possible to push command – source association for processing in commands execution thread (to my mind, useful feature). Source in this case can be any object not necessarily LCDUI component on which this command occurred (including it of course). Because of this, it is even possible to push any complementary argument with command. Besides, command can be any object and can hold attributes too. This library also gives possibilities to associate key (game action) or sequence of keys and/or game actions with command and to issue found command to the commands execution thread.
The use of this library is pretty simple. Developer should
subclass MoXMIDlet
(MIDlet abstract subclass) and override method
MoXMIDlet.xCommandAction(
Object cmd, Object src). This is the only method of XCommandListener
interface, that any class interesting in processing of command events should implement.
The commands processing should be
putted here. Then as usual define needed Commands and instantiate
needed components. Set subclass of MoX
Midlet as CommandListener
or/and ItemCommandListener
or ItemStateListener
of above components. Thats all. Method MoXMIDlet.xCommandAction(
cmds, src) will be called serially for each command event
in thread separate from AMS callback thread. For example:
//Define Commands.
...
private static final Command TEST = new Command("Run", "Run Tests",
Command.SCREEN, 32);
...
// Initiate components.
...
Form mainForm = new Form("AppName");
...
// Set this as CommandListener of component.
...
mainForm.setCommandListener( this);
...
// Override XCommandListener xCommandAction( Object cmd, Object src) method.
public void xCommandAction(Object cmd, Object src)
{
...
if (cmd == TEST)
{
this.clearResult(this.suite.countTestCases());
this.suite.run(this.getTestResult());
}
...
}
...
Note For ItemStateChanged events command will be
MoXMIDlet.ITEM_STATE_CHANGED and source will be item
which state has changed.
It is also possible to process commands in AMS callback thread too. To do this just override action method of respective CommandListener, put commands processing code to be executed in AMS callback thread first and then call super method. For example:
...
public void commandAction(Command cmd, Displayable src)
{
...
if (cmd == STOP) this.stopThread();
else if (cmd == START) this.startThread();
else super.commandAction( cmd, src);
}
...
To push command events (command - source association) to commands
execution thread. Just call MoXMIDlet.pushCommand(
Object cmd, Object src) method. Remember command can be any object and
is tested based on identity, Source can also be any object. It needs
not be only Displayable or Item on which command has occurred. It can
be even complementary argument to command. For example:
in component:
...
protected void keyPressed(int key)
{
switch (this.getGameAction(key))
{
case Canvas.FIRE:
this.owner.pushCommand( FIRE, myBook);
break;
...
}
...
}
in MoXMIDlet
...
public void xCommandAction(Object cmd, Object src)
{
if (cmd == FIRE) ((Book) src).burnIt();
else
...
}
...
To associate key (game action) or sequence of keys and/or game
actions with command developer should should
instantiate and configure KeyProcessor (class that looks up command based on sequence of keys), subclass XCanvas
(Canvas subclass) implementing needed rendering behavior and supply it with KeyProcessor and
Executor (e.g. from MoXMIDlet.getExecutor()).
For example:
... KeyProcessor kProc = ... ; ... XCanvas canvas = new XCanvas(); canvas.setExecutor( this.getExecutor); canvas.setKeyProcessor( kProc); ...
Thats all. All key events will be translated to commands and issued to the commands execution thread.
The simplest way to instantiate and configure KeyProcessor
for associating key and/or game actions with commands is by
KeyProcessor(Canvas canvas,
int[] keys, Object[] cmds) constructor. This constructor
takes two arrays as parameters, The first is an array of key codes
and/or game action constants and the second is an array of commands.
For example:
KeyProcessor kProc = new KeyProcessor( canvas,
new int[] { Canvas.DOWN, Canvas.UP, Canvas.KEY_NUM0},
new Object[] { NEXT_LINE, PREV_LINE, TO_TOP});
// this will associate game action DOWN with NEXT_LINE command,
//game action UP with PREV_LINE and key '0' with TO_TOP command.
It is also possible to associate a sequence of commands and/or game
actions with command. This is based on introduction of alt keys (alt
game actions), that can be associated with other KeyProcessors.
Next query of KeyProcessor, after alt key
(alt game action) was activated, will look up key or game action at KeyProcessor
associated with previous alt key (alt game action), To instantiate
and configure KeyProcessor in this case better to use
KeyProcessor(Canvas canvas, int[] keys, Object[] cmds, int[] altKeys,
mome.keyb.KeyProcessor[] processors) constructor. For
example:
KeyProcessor kProc2 = new KeyProcessor( canvas, new int[] { Canvas.DOWN, Canvas.UP},
new Object[] { TO_BOTTOM, TO_TOP});
// this will associate game action DOWN with TO_BOTTOM command and
// game action UP with TO_TOP command.
KeyProcessor kProc = new KeyProcessor( canvas, new int[] { Canvas.DOWN, Canvas.UP},
new Object[] { NEXT_LINE, PREV_LINE, TO_TOP}, new int[] {Canvas.KEY_NUM0},
new KeyProcessor[] { kProc2});
// this will associate game action DOWN with NEXT_LINE command, game action UP with PREV_LINE and
// key '0' with kProc2 processor.
// key sequence '0', UP will result to TO_TOP command and '0', DOWN to TO_BOTTOM.
KeyProcessors can be nested any number of times (if there is a need).
Cycle chaining of KeyProcessors (direct or indirect) is detected via
IllegalStateException
thrown from method KeyProcessor.process(int key), when
cycle is actually reached.
KeyProcessor can be used independently from XCanvas.
To look up command based on key code just call KeyProcessor.process(int key)
method. Key - command association can be looked up by calling method
KeyProcessor.process(int key) with key code. Game action - command
association can be looked up by calling the same method with key code mapped to
the given game action or with game action directly.
MoMELib has, of course, some other useful features. If you are interesting, look at MoMELib Home Page "http://momelib.sourceforge.net". MoMELib is ,of course, free software. It is available under Common Public License.
Best regards.