Introduction

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.

SourceForge.net Logo