Logo Computer scientist,
engineer, and educator
• Articles • Articles about computing • Articles about software development

Mobile application development: Android compared to J2ME

Android Android is a Linux-based system developed by Google, and primarily aimed at mobile handsets and other portable devices. Android provides a Java-based development platform for applications which, like Apple, it likes to call 'apps'. The ability to deploy applications written in other languages remains uncertain, as does the suitability of Android for devices much larger than a PDA. At the time of writing, Android 2.2 is starting to appear on 10-inch Internet tablets, against Google's opposition. Google claimst that Android 2.2 is completely unsuitable for tablets, which is probably true. Android 2.2 presents what is essentially a single-tasking user interface, not very different in appearance from Qtopia or PalmOS. There is no window manager or equivalent, and users do not (in principle) control the lifecycle of applications. Each application's user interface is sized to fit the screen, regardless of its contents.

Java 2 'Micro Edition' (J2ME) is a Java-based application platform for mobile devices developed by Sun Microsystems, as it then was. Unlike Android it is not a complete platform, just a virtual machine (VM). Device vendors are, in principle, free to implement the J2ME VM however they like. Some devices supporting J2ME are Linux based; many are not. J2ME is actually a set of specifications, for platforms of varying sophistication. In practice, most mobile phone handsets and PDAs have never supported anything other than the pair of specifications called 'MIDP/CLDC' — the very bottom rung of the J2ME ladder. If your cellphone is 'Java enabled', it almost certainly supports the very limited MIDP API set. MIDP2.0 offers an advance on the earlier 1.0 release, but both are very limited compared with desktop Java platforms. This makes J2ME of dubious value in modern, powerful handsets, which typically have high storage capacity and high-resolution screens. MIDP is really aimed at devices of the same vintage as the Palm 3 PDA. In fact, the MIDP storage model is based on 'record stores' — a concept that maps exactly onto the PalmOS storage model.

In terms of positioning, the Android development model is more like the J2ME 'Personal Profile', although it has similarities with MIDP2.0, specifically in lifecycle management and media support. Android Java has a fairly sophisticated (entirely proprietary) user interface model, albeit still essentially single-tasking. Programs are allowed to read and write files, open network connections to arbitrary hosts, and manipulate the device hardware (subject to certain security restrictions — see below). Significantly for many applications, Android is not an integer-only plaform, as CLDC is. The lack of floating-point support essentially precludes common J2ME implementations being used for scientific or mathematical applications.

So in many respects Android Java feels like a much more modern platform than J2ME. However, PersonalJava and Jeode were offering a full Java JVM for mobile devices more than ten years ago — it's perhaps unfortunate that these Java implementations failed to address the security concerns that made MIDP so much less worrying for device vendors. If you think of Android Java as MIDP with a full Java API and some hardware access, combined with a modern, declarative user interface model, you won't be far off the mark.

Because MIDP2.0/CLDC is the dominant implementation of J2ME in the mobile world, I won't be discussing any other flavours of J2ME in this article. When I say 'J2ME', I mean the kind of Java implementation you're likely to find on a Nokia or Sony-Ericsson phone handset, which will be MIDP2.0-based.

Java language issues

Both J2ME and Android Java applications are recognizable Java programs, made from assemblies of Java classes packaged into archives. A Java developer won't find any surprises in the language syntax or structure. Both J2ME and Android development use the Sun (as it then was) Java compiler tool to translate source code into Java byte-code, although raw byte code has to be transformed in both platforms (see below). In both platforms, the standard API library supplied with Sun's Java implementation is not used. Instead, programs link against the more limited API libraries supplied with the respective development packages. The Android Java API set is not related to J2ME or to desktop Java, but is derived from the open-source Harmony project.

Both J2ME and Android API sets include many of the well-known utility classes in java.util, such as collections and text formatting. Neither contains any of the standard desktop user interface classes (java.awt), as might be expected. Android contains a full implementation of java.Math, in double-precision floating-point. However, and a little oddly, most (but not all) the proprietary Android APIs that use decimal numbers use single-precision float, not double. Presumably this is for efficiency purposes, but the conversion between double and single-precision can cause a bit of head-scratching. J2ME developers are spared this complication by the simple expedient of leaving out floating-point support altogether.

The Android APIs include java.sql, and there are JDBC drivers for the built-in Sqlite database implementation. Surprisingly, the Android APIs also include javax.sql, leaving open the possibility of pooled database connections to remote servers. A Java crypo provider is also included, so you can use javax.net.ssl, etc.

Unlike J2ME developers, who have to contend with only two varieties of the MIDP API set — only one of which is in routine use — at the time of writing there are no less than ten Android API levels. Matching the API level to a specific Android version number is itself non-trivial, and it's very easy to write a program that will only run on a limited range of hardware. With increased support for larger devices like Internet tablets, this situation is unlikely to improve.

J2ME and Android Java are alike in that neither platform runs Java bytecode directly. Both platforms require additional transformation steps that are unnecessary with full-scale Java virtual machines. In the case of J2ME, this process is called pre-verification. Pre-verification encapsulates a number of steps that would ordinarily be done at application load time, and so speeds up application loading on low-power devices. However, the instruction set used in J2ME byte-code is essentially the same as in desktop Java.

In contrast, Android's JVM implementation, called Dalvik, uses a different instruction set from Sun's. Therefore a substantial transformation has to be applied to the compiled Java byte-code. The result is a 'DEX' file, which can contain the transformed versions of multiple compiled .class files.

In both J2ME and Android development, the compilation and transformation steps are fiddly, and difficult to accomplish at all without the use of vendor-supplied build and code management tools. More on this specific issue later.

In summary, both Android Java and J2ME provide a programming language and API set that will be familiar to Java developers of any background. However, there are substantial omissions from the API sets in both cases, compared to desktop Java, and compilation is only the first step in a more complex build process.

User interface model

Both J2ME and Android provide a user interface model that is very different from the more familiar AWT and Swing. However, although all the names are different, the J2ME and Android user interfaces are conceptually very similar. Knowing one, it's not difficult to use the other, provided you have the API references to hand. Both user interfaces are form-based, single-tasking, and layout-managed.

A form is a collection of data entry elements that occupies the screen. J2ME actually uses the term 'form', where Android prefers 'activity'. Android forms are implicitly stacked — that is, when one form (activity) invokes another, the invoked activity is considered to be 'on top', and the only way the user can navigate is up or down the stack. J2ME applications are, in practice, very often stacked the same way, but the implementation of the stack is left to the developer. In both cases there will be a 'main' or 'home' form which starts the application. In J2ME this is selected programmatically, in Android in an XML file. In both cases, the active form (or activity) occupies the whole of the accessible screen — there are no 'windows' in the sense that a desktop programmer would understand the term.

Android activities are a bit more than simple forms — they are self-contained units of interaction. There is a well-defined protocol by which activities pass data from one to another, and the strict encapsulation of activities means that an application can, in principle, invoke activities from a different application. An application that needed the user to end a geographical location, for example, could invoke a particularly activity in a mapping application to collect the data. To relieve dependence on specific applications, the cross-activity invocation protocol allows the target activity to be specified in terms of a textual description, rather like a URI. This is a very interesting concept, although it's not clear that many developers have so far taken advantage of it.

Another rather peculiar consequence of the encapsulation of activities is that, if a specific activity fails with an exception, it does not normally cause the whole application to fail. Typically the user will see the notorious 'Force close' dialog box, and then the faulty activity will just be popped off the stack and the underlying one re-presented as if nothing had happened. Whether this is a good thing or a bad one is arguable. Although activities can, and will, invoke one another, they are very loosely coupled, and this takes some getting used to.

By single-tasking I mean that the user does not normally see more than one form at a time, regardless of how many applications are running. In fact, both J2ME and Android hide the concept of 'running' from the user altogether. Applications are not supposed to provide the user with a way to close them — the system is supposed to do this according to resource demand. What this means is that when a user navigates away from your application to a different one, the system may shut down your application completely, and re-invoke it later. As a developer, you don't really get much control over this process, and you're expected to maintain your application's state so that it can be stopped and started at will by the system without irritating the user. Both J2ME and Android provide specific methods for applications to save their states, but the developer has to remember to use them. In both Android and J2ME, the developer must implement methods that the platform will call back on when the application's lifecycle state changes.

Because the user interface is essentially single-tasking, the developer does not have to worry very much about handling situations where part of an application's screen is obscured by another application. If the application is running, it is visible, and the whole screen must be drawn. Android takes this you-own-the-screen concept to extremes. For example, if your application is running and the user changes the screen orientation (by physically tilting the device from portrait to landscape, for example), Android will shut your activity down and re-start it with the new orientation.

Both J2ME and Android are layout-managed, meaning that the developer specifies the relative positions and sizes of objects in the user interface, and the user interface manager chooses the exact sizes and placement. This process will be familiar to Java desktop developers and, indeed, to anybody that has developed for Unix using any programming language. It's more of a shift for Windows programmers, who have got used to specifying exact screen layouts.

Android's layout manager, while conceptually similar to J2ME's, is quite a bit more flexible. This is only to be expected, as we've moved to larger, higher-resolution screens. J2ME has essentially only one, layout model — the screen is filled top-to-bottom in reading direction. Android's model allows for nested layouts including simple flows, tables, and relative-positioning (that is, specifying that element X should be to the left (say) of Y and below Z).

Layout management is pretty essential for a platform where applications have to be able to adjust to screens from a phone LCD display to a widescreen monitor. However, although if you're careful you'll get a workable user interface on all platforms, most likely you won't get a perfect user interface on any platform. It's surprising how easy it is for a screen layout that looks aesthetically ideal on one handset to be ghastly on another. Some compromises inevitably have to be made here. PalmOS developers had an easy time, with a screen that was standardised at 240x320 pixels. For better or worse, those days have gone. Both J2ME and Android developers do have to be aware how the layout manager works, and how to use it to get the best aesthetic compromise across a range of screen types.

The declarative user interface

Both J2ME and Android allow a user interface to be built up programatically. The process will be a familiar one to anybody who has worked with modern programming methods — the user interface consists of programming-language objects, which are linked together via API calls to form an object hierarchy. J2ME calls the user interface objects 'items', while Android prefers the term 'views', but conceptually they are the same thing. Both user interface models are, predictably, event-driven. This means that the application makes method calls on UI objects to register interest in events generated by those objects — button clicks, text changes, whatever.

Where Android is significantly more modern than J2ME is that its development model favours declarative user interface specification, with the layout specified in an XML file. This process will be familiar (surprisingly familiar, I expect) to anybody who has worked with GTK's declarative user interfaces, especially the Glade toolkit. It's also not very different from the 'resource files' that Windows developers will probably be familiar with. Elements are nested in the XML file exactly as they will be nested in the object hierarchy. This is a very convenient and natural way to construct a user interface.

However, the link between the declarative user interface and the application code is not particularly natural or visible. For most developers, this link is hidden behind the platform vendor's software tools, and to some extent it's only a problem if you want to use different tooling. Nevertheless, idiomatic Android code does look a bit odd to traditionalists. Consider this snippet of code, which attaches an XML-based user interface specification to an activity:

  @Override
  public void onCreate(Bundle savedInstanceState)
  {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
  }
The class R is not defined anywhere in the source code of this activity class. Indeed, it is not defined anywhere in the application. The variable R.layout.main links, in fact, to a file, res/layout/main.xml. And the file must be called main.xml, and it must be in the directory res/layout. At least, it must unless you're willing to rewrite all the build tools. The implicit filename-to-variable mapping also applies to links between XML files. For example, you can include an image from a file into a specific place in a form by referring to its filename, provided you use the correct directory structure. In fact, you can include multiple versions of the same image, to suit displays of different resolution, and the system will take care of presenting the correct one.

It has to be stressed that this implicit use of filenames and a particular directory model is a feature of the build tools supplied by Google, and not necessarily of the Android platform itself. What's going on behind the scenes is that the tools are parsing the XML files and building Java byte-code to implement the UI features. So, for example, all UI layout files end up in a class called R, whose static variables can be referenced by the application. This is all very well, but the process creates at least three complications for the developer.

First, in order to write idiomatic code, and refer to your user interface elements as R.layout.whatever, the build tools need to know your user interface package name. The package name becomes an integral part of the application structure. We can refer to R.whatever only because R is a class in the same package as the Java class that uses it. Structuring a complex user interface into different Java classes is not at all straightforward.

Second, Android development depends on knowing (and remembering) a whole heap of naming and file layout conventions.

Third, the build process is (as might be inferred from the above considerations) pretty complicated, and not well-documented. You're more-or-less constrained to setting up your project file structure in the Google-approved way, whether it fits your code management and workflow practices or not. The 'Google way', for example, favours including resources for platforms of different capabilities (e.g., different image sizes) all in one file, and letting the device decide at run-time what to use. But if you prefer to build different versions of an executable for different platform classes, you're going to be fighting the build process all the way. It's worth keeping in mind that the approved target for all Android applications is the Google Android Marketplace, and the build process is set up to create applications compatible with that delivery model, for better or worse.

Security and sandbox

J2ME and Android both provide a 'sandbox' model of application execution, which attempts to protect the system and other applications from badly-behaved code. Java, being a system based on a virtual machine, is well suited for such an execution model. J2ME — at least as it is normally implemented — lacks a sophisticated model of application permissions. Consequently, J2ME applications cannot normally read or write files, or open network connections to arbitrary places. This is one of the reasons, I suspect, why J2ME enjoyed a much greater uptake than PersonalJava — mobile handset and PDA vendors simply did not want the hassle and expense of supporting a truly open platform. In Android, Google has had to make an uneasy compromise between providing a platform that will support powerful, feature-rich applications, and a platform that hardware vendors will feel safe providing to users.

One feature of this compromise is that Java applications run under different user accounts in different operating system processes. This allows a measure of sandboxing to be provided by the Linux system itself. This still leaves the issue of access to hardware and network resources. The approach Google has adopted is to force applications to declare the access privileges they require. This declaration is in the manifest file which also specifies the required API level, etc. If an application tries to carry out an operation that requires a specific privilege, and that privilege has not been declared, then the operation will fail. A device is free to reject applications that declare particular privileges, or give users the option to do so.

This is not by any means a perfect security model. For example, privileges cannot be elevated at run-time. So if an application requires access to, for example, GPS positioning, it must declare it, even if the application may never use GPS, and the user's device may not even support it. However, for all its limitations, it is a more open development model that J2ME offers, and more open than many other virtual-machine based environments (e.g., Adobe Flash).

Building and Packaging

Both J2ME and Android expect applications to be delivered in the form of JAR (zip) archives. Android calls its archives 'APK' files, but they're plain JARs and the contents are recognizable. There is a particular defined layout within the archive for both platforms — one that is understood by the vendor's development tools. Manual or scripted packaging is just about possible using J2ME — the archive format is well-defined and the only assembly step that is likely to be unfamiliar is the pre-verification. Assembling an Android application without the vendor's tools is less agreeably contemplated. Not only does the process involve unfamiliar steps like DEX transformation, there is also the conversion of XML user interface definitions into byte-code to contend with.

The Android SDK includes a proprietary tool that will build a directory structure and an Ant build script, given certain project settings. Thereafter, you should only need to run Ant to build everything up to date. When using this tool, you have to settle on, among other things, package names for your main components up front — they are exceptionally hard to change later.

There is also an Android plug-in for the Eclipse IDE tool, although I haven't used that and can't comment on it.

In practice, the Ant-based build system works pretty well, provided that you do things the Google way. The directories in the generated project have specific purposes, and you can't change the layout or, in some cases, even add new directories, without upsetting things. In addition, my experience has been that dependencies between the XML-based UI files and the corresponding Java classes are not always managed very well by the tool — the two sets of byte-code got out of sync, leading to peculiar and unpredictable failures of the application. I've found it necessary to do complete rebuilds more often than should really be necessary. It's certainly worth doing this if your application behaves bizarrely for no obvious reason.

Emulation and debugging

You can, of course, test J2ME and Android applications on a real device. Or, better still, a number of real devices. Methods for getting code onto a real device vary according to platform and manufacturer. Most end users will user some sort of web-based market to obtain their applications — Google offers the Android Marketplace, although there are other sources. For J2ME, a whole range of different market implementations are in use.

Because you won't (I presume) want to go through a Web marketplace to test your own code, you'll need to find a way to get the APK or JAR files onto the device, and in practice most devices do not make this easy. You can in some cases copy the archive onto the device over a USB connection, or through a memory card, but that still leaves the problem of getting the device to recongise it. A number of third-party file managers will launch the package installer if pointed at an application archive. An alternative for Android, albeit a complicated one, is to use the Android Debug Bridge (ADB, supplied with the development kit) to push an APK over a USB connection. It's difficult to give specific instructions about installing applications either on Android or J2ME. Some Android devices do not even allow code to be installed except via ADB or the marketplace. Some devices require users specifically to configure the device to install these 'unsafe' applications.

One method of installation that does generally work is to put the application on Web server, and point the device's Web (or WAP) browser at it. This is fiddly to set up in the first instance but, once set up, reasonably quick to use. Of course, it requires that you have access to a Web server that the device can reach.

In practice, most developers prefer to test their code on an emulator of some sort, not least because it makes it easier to collect debug output. Emulators are supplied for J2ME and Android, but the implementations are significantly different. At the time of writing, the only way to emulate the Android Dalvik JVM is on a complete, machine-level implementation of an Android handset. Google supplies such an implementation, which runs a compiled ARM-CPU build of the Android platform on a complete ARM platform emulator. On the one hand, this gives a highly realistic emulation — it's as near to running on a real device as you can get. On the other, the emulation is slow and resource-intensive — superficially it's like running Android Linux under a virtual machine on your development workstation, but it's not a virtual machine in the way that, say, VMWare is. The Android emulator uses QEMU to emulate the ARM CPU which, of course, most likely is not what your development workstation has.

J2ME emulators tend to simple Java applications, that implement the J2ME UI elements in Java code. This is much faster and lighter, but arguably not as realistic. Work is underway to port the Dalvik JVM to platforms other than Android, but that still leaves the problem of emulating the Android user interface if you want to test Android code. It seems that the Google full-emulation approach is likely to remain the only show in town for now, aside from using a real device.

Conclusion

Developing for Android is both similar to, and different from, developing for J2ME — and both are rather different from developing desktop Java applications.

Both Android and J2ME applications are based on Java classes, using standard Java syntax and a large subset of the Sun Java APIs. Both provide a form-based, layout-managed, single-tasking user interface, based on a hierarchy of objects. Both UI models are event-driven, and have a lifecycle managed by the platform. Both platforms depend on vendor-supplied development tools in addition to a Java compiler, and both expect applications to be packaged up in particular ways. Although Android and J2ME use different terms for almost everything, concepts mostly map neatly between the platforms — the developer merely has to learn an entire new vocabulary.

The most significant differences are around security and the use of declarative user interfaces. Android applications may be allowed access to files, hardware, and network connections, while J2ME almost always prohibits this. The declarative user interface tools are straightforward to use and effectively separate presentation from logic. But they do impose workflow and file layout restrictions on the developer which not everybody will be comfortable with.

Copyright © 1994-2013 Kevin Boone. Updated Feb 08 2013