Thursday, September 27, 2007

Over the last year I have transitioned from developing mostly desktop applications (Java SE) to mostly mobile applications (Java ME).

I describe here some of the unique aspects and challenges of Java ME development and some of the ways in which it differs from Java SE.

I hope that it might be interesting to developers who are curious about Java ME.

I refer multiple times to MIDP 2.0 which is, at the time of this writing, the version of Java ME that is installed on most new phones.

Tools

Many phone manufacturers provide entire IDEs for Java ME development. These are usually custom versions of Eclipse or Netbeans that come bundled with emulators for their devices. The problem is that these IDEs don’t play well together. The IDE from manufacturer X will not work with the emulators from manufacturer Y. This of course makes it difficult to build & test apps across platforms.

To remain neutral I use the standard Eclipse IDE with the Eclipse ME plug-in. The Eclipse ME plug-in is a free project run by Craig Setera and it does a pretty good job interfacing with most of the vendor tools / emulators.

Limited Resources

The obvious difference between the desktop and the mobile is the limited resources available to the latter. A MIDP 2.0 device can have a screen size as small as 96x54 and may have as little as 128K of heap space. Many phones have a strict limit on application size (128K is common) and wont even allow you to install a jar file larger than it. Java ME developers use several techniques to minimize jar file size. Two common methods are:

Obfuscation

An obfuscator is a tool that analyzes your jar file, removes unused classes / methods and renames the remaining ones. A class called “HighScoreManager” for example might get renamed to “a” and its method “updateHighScore()” might get renamed to “b()”. Obfuscation is used in Java SE primarily to make it more difficult to decompile. It is used in Java ME primarily to reduce jar file size. They can do a surprisingly good job at both.

Every class file has overhead so Java ME developers try to limit the number of classes they write. Unfortunately this encourages writing non-object-oriented C-like code. Most Java ME games today are implemented in just a few classes that contain thousands of lines of code.

One of the biggest challenges in mobile development is balancing good software design with small code size.

Limited APIs

The Java ME APIs are basically a very small subset of the Java SE APIs plus some phone specific stuff. This means that lots of useful classes and methods you have grown to love and depend on just aren’t available. As an example, MIDP 2.0 includes just 9 classes in the java.util package. If you want to use a StringTokenizer or TreeMap you will need to implement your own.

Java Fragmentation

The term “Write Once, Run Anywhere” isn’t used in Java ME circles for even the cheap joke.

Unfortunately the Java ME specs leave much up to interpretation. The look & feel of the UI widgets for example differ greatly across phones. One example is the MIDP 2.0 “Gauge” object. I've seen it rendered as a progress bar, an hour glass, and even as Duke (the Java mascot) waving at you! This inconsistency has led Java ME developers to ignore these widgets and implement their own from scratch.

Early versions of Java ME didn’t support things like accessing the phone book or controlling the camera. As a result each manufacturer began to introduce their own proprietary & incompatible libraries to support such features. Things have improved in this area with several industry-standard APIs (as JCP JSRs) but these remain optional and are only found in a subset of MIDP phones.

The Java ME runtime is different on every phone and each one has bugs. Since people generally don't update the firmware of their phones these bugs exist for the lifetime of the device. If one of these bugs affects your application you must either live with it, find a workaround, or drop support for the model.

These problems are so pervasive that solutions exist that enabled preprocessor support and the use of #ifdefs in Java code. I have thus far been able to avoid this path.

Installation

The way you install an application can differ greatly depending on the phone manufacturer, model, and even carrier.

The standard installation mechanism is called “over the air” (OTA). This is done by placing your application on a website and downloading it wirelessly (“over the air”) via the phone’s data network. This can be slow and depending on your data plan very expensive. Also if you are testing on multiple phones you will need service on each (or in the case of GSM models you will need to manually swap SIM cards which is tedious).

Some phones allow you to install applications over USB but the software and cables required vary greatly from phone to phone. I currently have at least 6 different desktop applications that can install apps on various phones and right now I have 5 different USB cords, all with different end connectors, plugged into my PC.

Some phones allow you to install over Bluetooth but this too can be slow and the Bluetooth drivers on the PC still have a habit of crashing.

Each time I want to test on a new phone I must first spend (waste?) time just figuring out how to install!

Debugging

Debugging on mobiles can be extremely difficult. Only some phones allow you to see output from System.out and only some support “On Device Debugging”. (On Device Debugging allows you to run a debugger on a PC and debug, over USB or Bluetooth, an application running on a phone).

I have seen cases where an application runs perfectly on an emulator but crashes immediately on the real device. Try debugging that without System.out or a debugger!

Signing

On most phones many of the interesting APIs, e.g., phone book, camera, and Bluetooth are considered “protected” and their use requires that the application be signed.

Unfortunately getting your application signed can be an extremely difficult hurdle to pass (especially for small companies and hobbyists).

The signing process and the cost vary greatly across phones and carriers. Some carriers require that your first develop a “business relationship” with them. This can be as simple as paying a yearly fee or as complex as convincing them that your product is unique and worthy and then signing a revenue sharing deal with them.

Many of the challenges of mobile development are so very frustrating because they feel like old problems that we have already solved or outgrown years ago. Who today wants to deal with the problems of limited memory or spend time re-implementing a BufferedInputStream?

However annoying and frustrating it can be all of the challenges described are being solved quickly and mobile development is an area with lots of new interesting problems and enormous potential. It is becoming increasingly clear that we are entering a mobile development boom so the next few years in this industry should be an interesting and exciting time.