Java programming for Apple's iOS devices is not only possible but it's getting easier all the time. Steve Hannah surveys the recent evolution of the Java iOS landscape, then introduces five open source Java iOS tools. Find out how Avian, Codename One, J2ObjC, RoboVM, and XMLVM resolve the challenges of Java iOS native client development for developers who are ready to go mobile. (Includes an introductory section on garbage collection vs reference counting in Java iOS application development.)
This paper discusses both new and traditional approaches to legacy modernization, while highlighting a
LEARN MOREAs a Java developer I have felt somewhat excluded by the wall around Apple's iOS garden. Until recently, if I wanted to write an application for an Apple device such as the iPhone or iPad, it meant leaving the Java ecosystem behind and coding in Objective-C. The barriers to entry were both political and technical, but Apple's decision in September 2010 to relax the restrictions on its development tools effectively dissolved the political constraints. Since that announcement, a handful of open source solutionshave emerged that allow developers to write iOS applications in Java. I briefly introduce five of those tools in this article:
Each of these tools can be used to write Java code that can be run directly on an iOS device natively. I omitted the myriad of server-side Java EE solutions that publish HTML5/CSS/JavaScript applications to a Web browser or similar client on the device. There's enough to say about tools in this category for another complete review. Note, too, that Oracle has recently announced its intention to open source the iOS versions of JavaSE Embedded and Java FX. Both of these platforms are potentially viable pathways from Java to iOS, especially when combined with the recently announced proposal to enhance the JNI specification with support for statically-linked native libraries.
Strategies for running Java on iOS
Three categories of tools allow Java developers to write to iOS devices:
- Server-side technologies (such as Vaadin, Tabris, andICEmobile) run Java code on a server and publish either HTML5/CSS/JavaScript or JSON to be rendered on the device via a web browser.
- Client-side technologies, discussed in this article, run Java code directly on the device using a compile-time conversion to a native binary.
- Hybrid technologies like Oracle ADF deploy both server-side and client-side Java components.
The client-side tools profiled in this article are especially geared to creating native Java apps that run on iOS devices such as the iPhone and iPad. They enable developers to write code in Java that is then compiled into a native iOS binary. Although their user interface and integration strategies vary, each tool provides a native method mechanism that allows you to call Objective-C and C APIs from Java code.
Avian, for instance, uses standard JNI, whereas XMLVM uses its own native method system, which is similar to JNI but provides stronger compile-time validation. RoboVM and XMLVM also provide Java wrappers for many of the iOS APIs (such asUIKit), so that you can develop an entire iOS application in Java without once delving into Objective-C. Codename One solves the user-interface puzzle by providing its own pure-Java lightweight UI toolkit, but it also supports native interfaces that allow you to talk to Objective-C from Java. J2ObjC's answer to native interfaces is similar to GWT's, in that it allows you to define native Objective-C implementations for Java methods inline using OCNI.
Note that this article assumes that you are familiar with the basic architecture of iOS tooling and applications.
Codename One
Codename One provides a full-development toolchain for writing iOS applications in Java. Applications written in Codename One can be deployed to a variety of devices, including Android, iOS, BlackBerry, Windows Phone, and J2ME (although some features may not be available on lower powered devices). Codename One comes the closest of any Java mobile toolkit to supporting Java's "write once run anywhere" premise. Two components are key to enabling it to work across multiple platforms:
- Lightweight UI toolkit: The cross-platform UI toolkit is similar to Swing. Codename One UIs are pluggable and themeable, enabling them to take on the look-and-feel of a native device. A Codename One app running on an Android device will match the Android look and feel; on iOS it will match the iOS look and feel.
- Pluggable implementation: All of Codename One's platform-dependent code is contained in a single class that can be easily overridden to support another platform. This makes the framework extremely portable. The default iOS port is is currently XMLVM, but it isn't difficult to change it to Avian or RoboVM. (In fact, I created an Avian port as a research exercise without too much difficulty.)
Figure 1 shows a typical path from Java to iOS using Codename One.
Figure 1. The Codename One toolchain (click to enlarge)Codename One comprises a Java API, the Codename One Designer tool (a WYSIWYG GUI builder), a simulator that allows you to test your application, and a cloud build server that allows you to build your application for any device. One nice thing about the cloud build server is that you don't need to have proprietary tools such as the Apple developer tools installed. Codename One's build server frees you from the platform dependency typically associated with iOS development. With Codename One, you don't need to have a Mac to develop apps for iOS anymore.
Figure 2. Codename One application architectureIt's also possible to set up your own build environment and not depend on the Codename One cloud server. Doing that would entail creating a multi-step build process involving javac
, a build tool such as Ant, XMLVM (see below), and Xcode.
RoboVM
RoboVM is brand new (in version 0.0.1 at time of writing) and much of the Cocoa/Objective-C bridge is unfinished, but it appears to be full of potential. RoboVM is a command-line tool that you can use to compile Java .class files into native machine code. It provides an AOT (ahead-of-time) compiler based on LLVM, C bindings, and an Objective-C bridge, which lays the foundation for pure-Java applications to be deployed on iOS. It also includes a full set of Cocoa bindings (which should enable you to use the iOS native APIs directly from Java) and an Eclipse plugin that allows you to compile and run on the iOS simulator directly from Eclipse.
Figure 3 shows a typical toolchain path from Java to iOS using RoboVM.
Figure 3. The RoboVM toolchainRoboVM was designed as a path for Android developers to reuse their business logic code on iOS. You can either create the UI programmatically in Java, or you can use Apple's Interface Builder and Xcode to develop the UI and then copy the Nib file into your RoboVM project and link it up to your Java classes. It includes some nifty annotations that enable you to register Java classes with the Objective-C runtime and register Java methods to respond to Objective-C messages, so that you can define outlets for a Nib file inside a Java class. Figure 4 shows RoboVM's application architecture.
Figure 4. RoboVM application architectureRoboVM is still very young, so if you decide to try it out you should expect to get your hands dirty with its API. You will likely run into missing methods for which you'll need to generate bindings. All of the tools are present but documentation at this early stage is still scarce, so you may have to go through some cycles of trial and error.
Avian
Avian is a bit of a misfit in this group because it wasn't written to solve the Java iOS problem specifically. Instead, Avian is a lightweight JVM that includes a tool (bootimage-generator
) that compiles Java bytecode into native binaries that can be run on ARM devices like the iPhone. Avian's creator, Joel Dice, has published asample iOS application that serves as a proof-of-concept for building iOS applications in Avian.
Figure 5 shows a typical path from Java to iOS using Avian.
Figure 5. The Avian toolchainAvian doesn't provide specific bindings for iOS native APIs and it doesn't provide special APIs for building mobile applications. Any interaction between Java and the native environment must take place using JNI. Avian includes its own class library that is a more modular and portable subset of JavaSE, but it also allows you to build applications against OpenJDK 7. In order to reduce the size of the resulting binary, Avian uses Proguard to strip out dead code. Figure 6 shows Avian's application architecture.
Figure 6. Avian application architectureXMLVM
XMLVM has a much broader focus than most of the other tools discussed here because it aims to allow translation between many different languages. One such translation path is from Java bytecode to C source code, which is used to build iOS applications in Java. The typical XMLVM build process is to write some code in Java, compile it using javac
, then use XMLVM to convert the .class files into .h and .c files. These C source files are then added to an Xcode project and built into an iOS application, as shown in Figure 7.
XMLVM can be invoked from the command-line to perform conversion directly, or it can be invoked to create a skeleton NetBeans project that includes the appropriate libraries and build scripts to compile a Java application for iOS. The "Run" option actually creates an Xcode project with the translated C files, and opens it in Xcode. There you can debug the project, run it on a connected device (iPhone or iPad), or run it in Apple's simulator.
Like RoboVM, XMLVM contains Cocoa bindings that enable you to use native iOS APIs directly from Java, but they have many missing pieces. If you intend to use these bindings you should be prepared to pop the hood and possibly fill in some gaps yourself. Figure 8 shows an XMLVM application architecture using Cocoa wrapper classes for UI.
Figure 8. XMLVM application architecture using CocoaIf you want to include source that has been translated by XMLVM into an existing Xcode project, you can do so, but you will need to take care to add the appropriate build rules and initialization code for the garbage collector. You should also understand the implications of mixing garbage-collected code with reference-counted code. Figure 9 shows an XMLVM application architecture where the Java business logic has been compiled to C and included in an existing Xcode project.
Figure 9. A Java app compiled to C and ported to an Xcode projectJ2ObjC
J2ObjC has the narrowest focus of the tools introduced in this article. Open sourced late last year, it was designed by Google specifically to allow code to be shared between Java (Android and GWT) and Objective-C (iOS) projects. Figure 10 shows a typical path from Java to iOS using J2ObjC.
Figure 10. The J2ObjC toolchainJ2ObjC includes a command-line tool for converting Java source code to Objective-C source code. It also includes some Java annotations and a Java compatibility library for Objective-C. J2ObjC's narrow focus makes it an ideal choice if your goal is to share business logic between apps written inJava and Objective-C. Integrating code generated with J2ObjC with an existing Objective-C project is actually painless, and the close correspondence between the original Java class structure and the resulting Objective-C class structure makes it easy to use the APIs from Objective-C.
Figure 11. J2ObjC application architectureA key differentiator between J2ObjC and the other solutions is its preference for reference counting over garbage collection (that is, while it can use GC, it isn't supported on iOS). In addition to "transpile-time" management for reference-counting, and its support for manual reference counting and ARC via command-line flags, J2ObjC provides two mechanisms to help you deal with memory management in your Java code:
- Annotations to provide memory management hints (e.g.
@Weak
) - A memory profiling tool to help detect memory cycles, called
MemDebug
Garbage collection versus reference counting
While garbage collection makes our lives easier inside the Java world, it complicates them in the native world, where reference counting is the rule of the day. For instance, if you set a property of an Objective-C object to be a Java object, the garbage collector will know nothing about this reference. As a result, it may free the memory of the object if it is no longer accessible inside the Java world, even if the native world still needs it. The solution is to introduce your own form of reference counting for objects that you wish to maintain references to inside non garbage-collected structures.
ARC on iOS
Automatic reference counting (ARC) on iOS 5 and higher places the compiler in charge of counting references, rather than the developer. ARC works the same as garbage collection except that it doesn't handle cycles, for instance in cases where an object contains a reference to another object, which (transitively) contains a reference to the first object. In this case a "dead" cycle of objects would be freed when using garbage collection, but not when using ARC. The solution provided by ARC is to denote some references as "weak" references so that they don't affect the reference count for an object. This can effectively break a cycle and allow these cycles of objects to be correctly freed when they are no longer referenced. See Resources to learn more.
As an example, consider this snippet from an Objective-C application that includes some code generated by XMLVM:
@interface NotesListController
@property JAVA_OBJECT notes;
@end
Here we are declaring a property on an Objective-C classNotesListController
to be of type JAVA_OBJECT
. The problem is that Objective-C classes like NotesListController
are reference-counted by the Objective-C runtime, but JAVA_OBJECT
variables (the type used by XMLVM for all Java objects) are managed by XMLVM's garbage collector. Now, suppose you assign an object to aNoteListController
, like so:
self.notes = ca_weblite_crossmobile_shared_Note_getNotes__();
// calling static java method ca.weblite.crossmobile.shared.Note.getNotes()
The garbage collector has no knowledge that self.notes
contains a reference to the object that was returned from theca_weblite_crossmobile_shared_Note_getNotes__()
method, so, if there are no other references to that object in the heap, the garbage collector may delete it, even though theNoteListController
still has a reference to it, and still needs it. When NoteListController
tries to use this object later on, that action will trigger a memory access violation.
The solution alluded to above is to make sure that the heap always includes a reference to the object until it isn't needed anymore. We could do this fairly easily by creating a simple "Auto-release pool" in Java as follows:
/**
* Class to maintain references to Java objects in the heap.
*/
public class AutoReleasePool {
private static Map<Object,Integer> referenceCounts = new HashMap<Object,Integer>();
/**
* Increments the reference count for an object.
*/
public static void retain(Object o){
Integer count = referenceCounts.get(o);
if ( count == null ){
count = new Integer(1);
} else {
count = new Integer(count.intValue()+1);
}
referenceCounts.put(o, count);
}
/**
* Decrements the reference count for an object.
*/
public static void release(Object o){
Integer count = referenceCounts.get(o);
if ( count == null ){
return;
}
count = new Integer(count.intValue()-1);
if ( count.intValue() <= 0 ){
referenceCounts.remove(o);
} else {
referenceCounts.put(o, count);
}
}
}
Using this class, we would "retain" a JAVA_OBJECT
when it is set as a property of an Objective-C class as follows:
if ( self.notes != NULL ){
AutoReleasePool_release___java_lang_Object(self.notes);
// Release old object we don't need it anymore
}
self.notes = ca_weblite_crossmobile_shared_Note_getNotes__();
// calling static java method ca.weblite.crossmobile.shared.Note.getNotes()
if ( self.notes != NULL ){
AutoReleasePool_retain___java_lang_Object(self.notes);
// Retain the new object in the heap for GC
}
This paper discusses both new and traditional approaches to legacy modernization, while highlighting a
LEARN MOREJava iOS tools comparison
It is very nice to be able, once again, to write libraries in Java and deploy them everywhere. Each tool that I have discussed in this article has strengths and weaknesses. Codename One is the only Java iOS tool that currently comes close to the old "Write once, run anywhere" mantra, and it provides the most coherent development experience. I recommend checking it out if you're looking for a cross-platform environment for writing mobile apps.
If you would prefer to use Apple's native tools for your user interface, so that you can access all of Apple's cutting-edge libraries, then J2ObjC is a good option for sharing business logic.
I believe that XMLVM is among the most brilliant software development inventions of the past decade. Starting out as an academic research project that served as a proof of concept, it has enormous potential for bridging gaps between different platforms, and many of its most powerful uses may yet be discovered. XMLVM has already been used to successfully port apps from Android to HTML5/CSS/JavaScript and from Java to C, Objective-C, and C#. With a little bit of sweat, people-power, and ingenuity, perhaps it could be expanded into some sort of universal translator between all programming languages.
However, if your goal is simply to write Java applications and deploy them on iOS devices, XMLVM may not be the most practical tool for your needs. The save/compile/test cycle can be quite slow because the Java .class files could be converted into a couple thousand C source files that all need to be recompiled in Xcode. At its most practical, XMLVM currently serves as an engine for other tools like Codename One. (See the Codename One SVN repository for some great examples of XMLVM in action.)
RoboVM is a brand new Java-to-iOS tool that has much potential. Currently at release 0.0.1, it isn't quite ready for production as it is still missing many Cocoa bindings, and many features aren't yet implemented. However, it has a strong foundation in LLVM, and some very nice tools for interfacing with the native environment. I think it won't be long before RoboVM is ready for showtime.
Avian caters to a slightly different market than the rest of these tools. Its simple lightweight JVM makes it extremely portable to many different platforms, but its lack of Cocoa bindings and compatibility libraries makes it a less-than-ideal choice for writing Java applications for iOS. Avian could be used as the foundation for other tools that might allow developers to deploy Java onto iOS, however.
In conclusion
This article has been a brief survey of five open source tools for Java iOS development. You can learn more about the tools by visiting the Resources section and checking out examples on their individual websites. If you find that you love working with a particular tool, consider joining the developer community and contributing to making the project even better. All of the tools discussed in this article are open source, so any contribution you make will go toward building a more vibrant Java ecosystem.
Steve Hannah works as a software developer for Simon Fraser University in Vancouver, Canada, where he develops enterprise solutions using such technologies as PHP, MySQL, HTML5/CSS/JavaScript, and, of course, Java. He develops and maintains Xataface, an open source framework for building data-driven web applications, and has contributed to many other open source projects, both in documentation and code. He periodically writes about Java, mobile development, and other software related topics on his blog.
Learn more about this topic
- "Gosling: Static JNI could ease ports to iOS" (Ted Samson, InfoWorld, March 2013)
- "Java 8 on ARM: Oracle's new shot against Android?" (Paul Krill, InfoWorld, December 2012)
- More about reference counting on iOS: "Automatic Reference Counting on iOS" (José R.C. Cruz, Dr. Dobb's, May 2012).
- Programming tutorials for iOS beginners:
- "iOS Programming Basic: How Does the Hello World App Work?" (Simon Ng, AppCoda, April 2012 )
- "iOS Programming Tutorial: Create a Simple Table View App" (Simon Ng, AppCoda, April 2012 )
- "Cross-Platform App Development for iPhone, Android & Co. — A Comparison" (Heiko Behrens, October 2010, MobileTechCon 2010)
- More about J2ObjC:
- More about XMLVM:
- A quick how-to: "Compiling Java to Native with XMLVM, Linux and BoehmGC" (Thomas Trocha, Tom's Blog)
- About the Boehm-Demers-Weiser conservative garbage collector , used by RoboVM and XMLVM.
- Get the XMLVM red list: A listing of classes that are disabled in XMLVM.
- Read the XMLVM Manual
- More about RoboVM:
- "RoboVM Interface Builder integration" (Niklas Therning, RoboVM Blog, February 2013)
- Bro Documentation: RoboVM's C bindings
- Documentation for working on RoboVM