i-mode iappli and midlet development notesFor midlets you should get Sun's free J2ME development kit which includes a cell phone emulator. NTT DoCoMo also provide their own free version of this kit with an i-mode phone emulator. Both the Sun and DoCoMo emulator slow down execution to simulate a real phone. DoCoMo also provide excellent documentation in english (the API and a user manual) which is actually much better than Sun's documentation.
There isn't much difference when compiling code from
the command line
except that you should specify the -bootclasspath option when you
use javac to indicate the JAR file that contains the Java language classes you
are using. For i-appli's this is the "dojaapi.jar" file found in the DoCoMo
development kit. You should also remember to run the preverifying utility on any class files
you create. The preverifier is also provided in the
development kits. Preverifying adds a small amount of extra fat to the classes.
Writing code for cellphones in the same way you write for a normal processor just isn't on because of the size limitations and transfer speeds. DoCoMo's i-mode development manual gives some really good pointers on compression. The most important being to use the '-g:none' option with the javac compiler to take out debugging data. Remember you should always preverify classes after they have been put through a compression utility.
Obfuscation is another approach to compression and there are a number of products like the free 'RetroGuard' product and also the i-appli specific 'Java Blender' from Japan. However I found that neither product produced code that would actually run on a phone. In the final analysis I found hand obfuscation was the sure fire way to get the job done. Sounds gruesome but on a 5 to 10K class file size app not such a big job.
Reducing the final size of i-appli's and midlets is important.
Classes themselves are bulky items with a lot of fat and the less there
are the better. The best example of this is where we have an abstract
class which is extended into say three types of subclass. A much better approach
is to have a single class with a field int type which indicates
what one of the three it is and to have switch
statements that make the single class behave appropriately. Although as you will
see below there may be a better way than using a switch.
This is a good example of where we have to deconstruct the usual way of object orientated programming and go back to a much more old fashioned flat style. What I have found useful is to start with a lots of classes to get things working and then coalesce them.
If you have an array of a class and that class contains say three fields then it's better to eliminate the class and have three arrays for those three fields.
In graphics Iappli's and midlets you will have to have at least two classes. One
for the main application and one for the Canvas object on which you
draw.
Where possible declare variables as statics (mentioned in the DoCoMo notes). This will save extra bytes for every variable declared static. I have found that an int declared static saves 3 bytes.
It is much better to synchronize an entire method which incurs no overhead. Adding sychnronized blocks creates extra bytes.
When you run the garbage collector in J2ME it doesn't run in the background but goes out and finds all the dead stuff and reclaims it. Sometimes this can have surprising results that terminate the program so I prefer not to call it if possible. A better approach is to declare everything you need upfront in the program, re-use objects where possible, and if you must declare new objects during prolonged running of the code, declare sparingly so that memory usage creeps up very slowly.
Some phones like the Motorola i95cl are not fast so I recommend loading up a splash screen and displaying it in the beginning. Don't expect your program to initialize quickly - it can take a while. Image loading itself can be very time consuming so it's better not to load up a lot of images together at the program's beginning.
The J2ME spec says the phone has 3 font sizes. Don't assume this to be true. Phones like the Hutchison3G e303 from NEC don't offer three fonts. Also don't assume anything about the font size. Different phones will use different font sizes for the same Font class instance. However a program can use the J2ME Font class to ask how wide and how high any string will be and finding this out at runtime in the phone is really the only way to guarantee that things fit on the screen the way you want them to.
Once you decode an image from a file it takes up a lot of memory. If possible keep the image in it's original PNG, GIF or JPG format in the JAR for as long as possible. If the slight latency of decoding is acceptable then decoding when needed and then garbage collecting it when finished can be acceptable although it adds code while freeing up lots of memory.
Incredibly sound playing was never in the original MIDP 1.0 spec so every manufacturer has there own sound API extension. Some phones can play WAV, some can play MIDI and most japanese phones can play the Yamaha SMAF (.mmf) format which, like Midi, uses events to drive a synthesizer chip. May favourite is Midi because it's widely known, there are lots of tunes out there, it is really compact and it sounds great. The WAV format is best avoided because of the huge file sizes unless you must have true recorded sound. Midi can be converted to SMAF with tools found on the Yamaha site but conversion to SMAF from Midi doesn't always work and the resulting SMAF file can sound a bit bare. If you play Midi it usually has to be a single track midi 0 format type. Though Midi files sound great on phones like the Nokia and SonyEricsson there is a problem with limitations on how many instruments can play at the same time and occasionally notes will 'disappear'. To solve this there is a new Midi format called SP-MIDI, which prioritises the instruments and can be used in phones to get around the problem, it is described on Nokia's developer website. Personally I have only come across this note dropping problem once.
Also be aware there can be significant latency between starting to play a sound and the sound actually starting. The Siemens '55' series phones have huge latency times for bigger Midi files. Even the newer Nokia 6230 has fairly long (100's of millis) latency for Midi.
Such tools as my own MidiSqueezer can reduce Midi sizes for smaller Midlets.
Parameter passing in method calls requires time and code. Do as little parameter
passing as possible. Lets say a lot of classes have to read from the same
InputStream. Instead of passing the InputStream around as a
parameter it saves a lot of byte code to have a static method somewhere which
reads whatever you want and call that static method from the classes that need to
read the stream.
This use of static methods and fields is generally a good idea as stated previously. Wherever there is common code then make it a static method. If classes need to share data then make it a static field.
A boolean variable is false by default. Explicitly intializing it will cost you 23 bytes.
If there is nothing to do after a case statement in a switch block we often use the break statement for elegance. However using return instead of break will save 3 bytes.
String constants
Specifically here I mean avoid any System.out.println
statements as they are bulky and apart from testing purposes aren't
going to do much on a cell phone. But by the same token, all strings
take up space including the names of classes, fields etc. A good
obfuscator can reduce these down to a single character (see above).
If there is a switch statement with three case
clauses then it came as a surprise to me that I could save nearly 40
bytes of code by coding them as a series of if...then...else...if...
statements.
I've been constantly surprised by the byte codes saved by seemingly minor changes
like this. The best way is to experiment. One of the other things I discovered was that
final static int statements take up a lot of space. Three can take
up a hundred bytes which can be saved by hardcoding in the values. (When I talk
about saving 100 bytes here I mean 100 uncompressed bytes, The actual saving
is less when the class files are compressed into a JAR file ready for downloading into
a phone).
Another example is coding x=x+y as x+=y which will save 4 bytes.
If you are doing direct drawing on a Canvas object then you will need
to be concerned about double buffering. However the i-mode phone will do the
buffering for you as long as you have the G.lock() method at the very
beginning of you paint(Graphics G) method
and G.unlock(true) at the end.
The midlet doesn't guarantee the presence of double buffering. You have a method
Canvas.isDoubleBuffered() which will indicate if you already have this
facility. If not then a standard offscreen image will have to be created and used.
Many I-mode screens now are coming out with 256 colors and 64 thousand true color
screens are starting to appear. There is no Color class in either the iappli or midlet API.
To set the color in an iappli graphics context
we use the method call setColor(int) however the int being referred to
is a device dependant value which you first have to get by using one of two static method calls:
Graphics.getColorOfName(int) is used when you want to use one of the standard
i-mode colors which have predfined constant values such as Graphics.red
Graphics.getColorOfRGB(int r,int g,int b) where you pass in the normal RGB
values between 0 and 255 to get the corresponding device dependant value.
The midlet relies on a much more straightforward G.setColor(int r,int g,int b) method call with
no Color class available.
Strangely there is no ability to draw filled polygons in the Midlet API although there is in the i-appli API. However Midlets does have a method call for drawing arcs which is absent in the Iappli API.
For i-mode the screen size is variable and this can be an issue for graphics. From what I can determine the smallest color screen size is 96x90 (mitsubishi) with the largest ranging from 120x130 to 95x255 (feedback on this point is welcome). Perhaps the best approach is to program for the smallest screen size and center you images on anything bigger.
I-mode phones only allow the creation of one thread in an application which is unsurprising given the processor speed of the phones. Again some back to basics programming is needed to achieve the effect of more than one thread. Essentially you need to write your own process scheduler which is what I did with CellSpark.
Images can be loaded into both midlets and i-appli's. Howvever it's important to remember that midlets will only support PNG files (which supports up to 2 million colors) and iappli's will only support GIF files (which supports 256 colors, although I believe some phones now also support JPEG). The image loading in both is made very simple by both the i-mode and midlet API.
The record managemt system is at the same time crude and very flexible. Crude because you can only read and write byte arrays, flexible because you know exactly what's in those bytes and the records can vary in length. My tips are:
ByteArrayOutputStream for writing the records and
ByteArrayInputStream for reading them. These classes can be treated like
true files and can shield you from
getting involved with the details of the written and read byte arrays.
addRecord for appending a record and
setRecord when overwriting an existing record.
Appearing on the latest Nokia phones (and part of MIDP 2.0) is the wireless messaging API which allows a midlet to send and receive SMS messages. On the Nokia's for receive by a midlet you must include the 'Nokia-SMS-Handler-1:' attribute in the JAD and the JAR manifest so that a midlet get woken up to process an SMS arriving on a certain port. My advice for using this API is:
receive
method to get a messages payload is mandatory but I also found that send in a thread
was recommended on the Nokia to avoid problems when there is an incoming and outgoing
message at the same time.
There are very few individuals making any money out of selling midlets. The best money is in selling the midlet as an embedded application to a manufacturer. Average pricing for this is about 35K euros for one game. Personally I would avoid competitions which stack the odds against you in that the organiser can pay what they like, down to and including zero for your brilliant idea.
