MoMELib is a J2ME library for development of J2ME business applications. It tries to overcome some drawbacks of J2ME and provides capabilities for commands processing.
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 - it is a great possibility of dead locks with AMS. The second case makes impossible to use serious code in 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 part 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 enhances usage of commands. It is easy to define a command. For example:
private static final Object NEXT_LINE = new Object();
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 issue it 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)
. The commands processing should be
putted here. Then as usual define needed Commands and instantiate
needed components. Set subclass of MoxMidlet as
CommandListener
or/and ItemCommandListener
or ItemStateListener
of above components. Thats all. Method MoXMIDlet.xCommandAction(
cmd
s, 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(
cmd, 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
,
subclass XCanvas
(Canvas
subclass) implementing rendering behavior needed
for your application 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. If
alt key (alt game action) is found method KeyProcessor.process(int
key)
returns KeyProcessor.ALT_KEY_ACTIVATED
constant. Next invocation of KeyProcessor.process(int
key)
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.
Developer also can instantiate KeyProcessor by use of
KeyProcessor(Canvas)
,
KeyProcessor()
constructors and configure it later by addKey(int
key, Object cmd)
, addAltKey(int
key, KeyProcessor processor)
methods.
Canvas
property is needed because
Canvas.getGameAction()
method is not static
.
I don't know why. To my mind, It should be static
(it is
stateless method). Because of that, it is impossible to convert key
code to game action without an instance of canvas.
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.