SourceForge.net Logo

[ Home | Documentation | Gallery | Download | Demo | Resources ]
[ Index | Tutorial | Sheets | Pack | JB | Hello | Paint | MicroIO ]

Doc (Hello)

This page is an introduction on how to write a JBit Module. I assume you are an experienced Java programmer with some basic understanding of the MIDP API. I also assume that you are able to compile JBit from source. This page is not specific to any particular IDE.

Preface

Why extending JBit when you can write your own MIDlet? Here is a scenario. You might be interested in the Bluetooth API, but not in writing a complete application. You could leverage JBit; it's a complete, real application that would benefit from many technology-related modules. In this example, a module that can import/export JB programs via bluetooth would be very useful.

JBit is fairly complex and, if you take a look at the JBit source code, you might even get the impression that JBit has not been written in Java. But here I hope to show you that when writing a module for JBit you can ignore most of the ugly, nasty details and focus on just what you are interested in.

Before we begin, you might wonder why the code of JBit is so weird. The main reason is my desire to support old phones; this widens the potential user base, but poses some serious space constraints on the MIDlet. One of the most effective tool to save space is to reduce the number of classes. Reducing the number of classes has also the nice secondary effect of presenting a meaningful view to an advanced user who looks inside the jar. I don't know; perhaps there are tools around that help you merging multiple classes into one, but I like to keep my developing enviroment simple. This might not be the perfect solution, but I'm quite happy with it.

Getting Started

Start from the JBit source distribution (configured as Standard) and edit build.xml, replacing the following lines:

<attribute name="JBit-Module-1" value="CPU" unless="jbitrt"/>
<attribute name="JBit-Module-2" value="VM" unless="jbitrt"/>
<attribute name="JBit-Module-3" value="Monitor" unless="jbitrt"/>
<attribute name="JBit-Module-4" value="Store" unless="jbitrt"/>
<attribute name="JBit-Module-5" value="Editor" unless="jbitrt"/>
<attribute name="JBit-Module-6" value="Demos" unless="jbitrt"/>

with the single line:

<attribute name="JBit-Module-1" value="Hello" unless="jbitrt"/>

you could add the following line instead:

<attribute name="JBit-Module-7" value="Hello" unless="jbitrt"/>

but removing the standard modules makes clear that they are not really required; if you want you can remove them (in addition to LibView.java, a library used by the Monitor and the Editor, and IO.java) from the src directory, otherwise they will be included in the jar.

Create the file Hello.java in the src directory:

import javax.microedition.lcdui.*;

public final class Hello implements Module, CommandListener {
}

Note that in JBit, unlike in most other MIDlets, the names of the classes are kept during obfuscation; which is kind of ironic considering that I've just written how important it is for JBit to save space. The pieces not obfuscated were taking about 200 bytes last time I checked; maybe this value has increased, but I don't think by much. These few bytes give us modules that can be indipendently developed/distributed and that can be assembled by the end user; I think it's a good trade-off.

Note also that JBit uses the default package, so Hello would be a bad choice for a public module; you are encouraged to choose a unique name (the name of the menu item can be different from the name of the class).

We will now complete the Hello class.

Address Space

The easiest code to write is the Address Space part of the Module interface; it is the easiest because most modules don't need it, so you can leave it empty:

public short get(int address) {
    return -1;
}

public short put(int address, short value) {
    return -1;
}

-1 is used to express the fact that the methods are not implemented (if you prefer you can use 0; nobody is going to call these methods anyway).

Operation Dispatcher

With the Operation Dispatcher part of the Module interface, things start getting ugly.

private Module jbit;

public Object opO(int opId, int iArg, Object oArg) {
    switch (opId) {
    case OP_GET_METADATA:
        return getMetadata(iArg);
    case OP_GET_DISPLAYABLE:
        return getDisplayable();
    }
    return null;
}

public int opI(int opId, int iArg, Object oArg) {
    switch (opId) {
    case OP_INIT:
        jbit = (Module)oArg;
        return init();
    case OP_ACTIVATE:
        return activate();
    case OP_DEACTIVATE:
        return deactivate();
    }
    return -1;
}

The opO/opI pair is a cheap mechanism that allows the dynamic invokation of methods without the space overhead of interfaces (also note that J2ME does not support reflection). The actual methods will be shown later.

The integer constants beginning with OP_ are replaced with their numeric values during obfuscation; this makes the Module interface and its implementations very small, but it means that care must be taken to keep binary compatibility between different versions of JBit. Also the operations are untyped, so it is very easy to make mistakes. Fortunatelly, you can just copy this code and forget about it.

JBitSvc

If you look carefully at the code above you can see that OP_INIT receives a parameter that is not passed to the init method. This is another ugly bit; not only a module exposes its operations via the opO/opI pair, but it also uses the opI/opO pair to access the operations provided by JBit.

private Display getDisplay() {
    return (Display)jbit.opO(JBitSvc.OP_GET_DISPLAY, 0, null);
}

private void back() {
    jbit.opI(JBitSvc.OP_REPLACE_WITH_SERVICE, 0, null);
}

JBitSvc is the group of operations ("service" in JBit jargon) implemented by the JBit module.

A module can get the Display using the OP_GET_DISPLAY operation; I assume you understand what Display is and how it is used.

Top level modules are activated when the user select the associated menu item. An active module can request another module to take its place using the OP_REPLACE_WITH_SERVICE operation. Most of the times this operation is invoked without specifying what module to activate, leaving the choice to JBit. JBit shows the main menu letting the user decide.

Metadata

JBit needs to have some (vague) idea of what the module is for:

private Object getMetadata(int metadataId) {
    switch (metadataId) {
    case METADATA_ID_LABEL:
        return "Hello";
    case METADATA_ID_TYPE:
        return new Integer(TYPE_TOP_LEVEL);
    case METADATA_ID_SERVICES:
        return new String[] {};
    }
    return null;
}
TYPE_TOP_LEVEL lets JBit know that this module should be included in the main menu; LABEL is the name of the menu item. Until you know JBit pretty well you don't have any reasons to expose services to JBit (as a case in point, if you browse the available services you will find SaveSvc; you might think of implementing it in your import/export module, but in fact SaveSvc SHOULD NOT be implemented by import/export modules).

Hello

The code above is pretty much the same for every top level module.

Now we can move on to the parts that are specific to this module:

private int n;
private Form form;

private Displayable getDisplayable() {
    return form;
}

private int init() {
    n = 0;
    return 0;
}

private int activate() {
    form = new Form("Hello");
    form.append("Activation #" + (++n));
    form.addCommand(new Command("Back", Command.BACK, 1));
    form.addCommand(new Command("About", Command.SCREEN, 1));
    form.setCommandListener(this);
    return 0;
}

private int deactivate() {
    form = null;
    return 0;
}

public void commandAction(Command c, Displayable d) {
    switch (c.getCommandType()) {
    case Command.SCREEN:
        Alert alert = new Alert("Hello 1.0");
        alert.setString("Written by Emanuele Fornara");
        alert.setTimeout(Alert.FOREVER);
        getDisplay().setCurrent(alert);
        break;
    case Command.BACK:
        back();
        break;
    }
}

I assume that this code looks straightforward to you; here are some random remarks:

Future Changes

JBit should be considered experimental; so you might have to adjust your code in the future. Keeping binary compatibility is not a priority yet, so if you distribute a module, please state the version of JBit you compiled it against. Here are some thoughts for future changes: