In the past years, mobile applications took the world by storm and already changed the way we use the internet for work or leisure. Various technologies emerged to create mobile apps and development processes start to consider mobile as first class citizens. But even though mobile already seems to be omnipresent, the future is just about to start. We're facing new generations of mobile devices like wearables or lots of mobile gadgets that make up the Internet of Things. We will be confronted with new types of user interfaces for displaying data as well as accepting commands. And we will recognize more and more companies going real mobile first. All this will influence the way we design, develop and test software in the coming years.
This InfoQ article is part of a series on the fast-changing world of Mobile technology. You can subscribe to notifications about new articles in the series here.
There are hundreds of thousands of mobile applications for nearly every purpose in the iOS or Android app stores. Usually they are created with Objective-C toolstacks for iOS devices and Java based for Android handsets. In this article we would like to show you two not so common ways to build native apps with Java and Xtend which help to share code between both worlds and simplify development.
Developing native iOS apps with Java and RoboVM
Mobile app developers targeting both Android and iOS face many challenges. When comparing the native development environments of these two platforms, i.e. the toolchains provided by Google and Apple respectively, one quickly finds that they differ substantially. Android development, as defined by Google, is based on the Eclipse IDE and the Java programming language. iOS development, according to Apple, on the other hand is based on the Xcode IDE and the Objective-C programming language.
These differences rule out any code reuse between the platforms. Also, not many developers are proficient in both environments. In the end almost every multi-platform app is developed using separate development teams and separate codebases for each platform.
RoboVM is a new open-source project with the ambition to solve this problem without compromising on neither developer nor app-user experience. The goal of the RoboVM project is to bring Java and other JVM languages, such as Scala, Clojure and Kotlin, to iOS devices. Unlike other similar tools, RoboVM doesn’t impose any restrictions on the Java platform features accessible to the developer, such as reflection or file I/O, and lets the developer reuse the vast ecosystem of Java 3rd party libraries. It is also unique in allowing the developer to access the full native iOS APIs through a Java to Objective-C bridge. This enables the development of apps with truly native UIs and with full hardware access, all from Java using tools familiar to Java developers such as Eclipse and Maven.
With RoboVM developing for both iOS and Android becomes less challenging; the same Java developers can build both versions of the app and a large part of the codebase can be shared.
How to get started
While RoboVM can be used in many ways, e.g. from the command line, or using Maven or Gradle, the quickest way to get started is probably using the RoboVM for Eclipse plugin.
Requirements
Before you install the RoboVM for Eclipse plugin make sure you have all the prerequisites:
- A Mac running Mac OS X 10.9.
- Oracle’s Java SE 7 JDK.
- Xcode 5.x from the Mac App Store.
Note that Eclipse MUST be run using Oracle’s Java SE 7 JDK or later. Apple’s Java 6 JVM will not work.
Install the RoboVM for Eclipse plugin
Once your system meets all the requirements installing the plugin is simply a matter of opening the Eclipse Marketplace from the Eclipse Help menu, searching for RoboVM and clicking Install Now.
Alternatively you can use the following update site:
Running a simple iOS app
We will now create a very simple iOS app. First of all create a new project by clicking File => New => Project.... Select the RoboVM iOS Project wizard in the list.
Enter IOSDemo as Project name, Main class and App name and enter org.robovm.IOSDemo as App id. Leave all other values at their defaults and click Finish.
Now let’s create a new class and call it IOSDemo with no package name. Copy and paste the code below into the newly created file replacing whatever Eclipse auto generated for you.
import org.robovm.apple.coregraphics.*; import org.robovm.apple.foundation.*; import org.robovm.apple.uikit.*; public class IOSDemo extends UIApplicationDelegateAdapter { private UIWindow window = null; private int clickCount = 0; @Override public boolean didFinishLaunching(UIApplication application, NSDictionary launchOptions) { final UIButton button = UIButton.create(UIButtonType.RoundedRect); button.setFrame(new CGRect(115.0f, 121.0f, 91.0f, 37.0f)); button.setTitle("Click me!", UIControlState.Normal); button.addOnTouchUpInsideListener(new UIControl.OnTouchUpInsideListener() { @Override public void onTouchUpInside(UIControl control, UIEvent event) { button.setTitle("Click #" + (++clickCount), UIControlState.Normal); } }); window = new UIWindow(UIScreen.getMainScreen().getBounds()); window.setBackgroundColor(UIColor.colorLightGray()); window.addSubview(button); window.makeKeyAndVisible(); return true; } public static void main(String[] args) { NSAutoreleasePool pool = new NSAutoreleasePool(); UIApplication.main(args, null, IOSDemo.class); pool.close(); } }
Finally, launch the app in the iOS simulator by right clicking the project you created and select Run As... ? iOS Simulator App (iPhone). This will run the app on a simulated iPhone.
To run the app on an actual device you would use the Run As... ? iOS Device App choice instead. Please note that this requires that the device has been set up for development. The process for doing so is out of scope for this article. Please refer to Apple’s documentation for further information.
Creating an IPA for App Store distribution
Provided that you already have your App Store distribution certificate and provisioning profile all set up creating an IPA package for submittal to the App Store is as simple as right clicking the RoboVM iOS project in Eclipse, selecting RoboVM Tools ? Package for App Store/Ad-Hoc distribution… and filling in the dialog that appears.
This will create a .IPA file in the selected destination folder which can then be verified and submitted to the App Store using the Application Loader application. The Application Loader application can easily be located using Spotlight.
Apple has some great resources that describe how to enroll in the iOS developers program and create the certificates and provisioning profiles required for App Store distribution.
Under the hood
The bytecode compiler
At the heart of RoboVM is its ahead-of-time compiler. This is a tool that can be invoked either from the command line, from build tools such as Maven or Gradle or from an IDE. It takes Java bytecode and translates it into machine code for a specific operating system and CPU type. Usually this means iOS and the ARM processor type but RoboVM is also capable of generating code for Mac OS X and Linux running on x86 CPUs (32-bit).
The ahead-of-time approach is very different from how traditional JVMs, like Oracle’s Hotspot, usually work. Such JVMs typically read in Java bytecode at runtime and somehow execute the virtual machine instructions contained in the bytecode. To speed up this process the JVM employs a technique called just-in-time compilation. In simple terms this process translates the virtual machine instructions of a method to native machine code for the current physical CPU the first time the method is invoked by the program.
Due to technical restrictions that Apple has built into iOS just-in-time compilation of any kind is impossible in an iOS app. The only alternatives are to use an interpreter, which is too slow and power consuming, or use ahead-of-time compilation like in RoboVM. The ahead-of-time compilation process takes place at compile time on the developer machine so at runtime, on an iOS device, the generated machine code runs at full speed, comparable to or even faster than code compiled from Objective-C.
By consuming Java bytecode rather than Java source code the RoboVM ahead-of-time compiler can, at least in theory, be used with any JVM language that compiles down to bytecode. Scala, Clojure and Kotlin are JVM languages already known to work. Another benefit with this approach is that RoboVM can be used with 3rd party libraries in standard JAR files without any need for the original source code enabling the use of proprietary and closed-source libraries.
Incremental compilation
The first launch of a RoboVM app, even an app as simple as the IOSDemo app, takes some time. When compiling an app the RoboVM compiler starts with the app’s main class. It will then compile all classes needed by the main class and then the classes needed by those classes and so on until all classes needed by the app have been compiled. This process also compiles the standard runtime classes such as java.lang.Object and java.lang.String. This is a one-time thing only. RoboVM keeps a cache of compiled classes and only recompiles a class when it or any of its direct dependencies have changed.
The benefit of incremental compilation and caching of the object files is that it keeps down compile times. By only including the classes reachable from the main class it also keeps down the size of the produced executable. In some situations (e.g. when loading classes using reflection) the RoboVM compiler is unable to determine that a class should be compiled. Fortunately the compiler can be instructed to always include a particular class or even all classes matching a pattern.
Android-based runtime class library
Any JVM needs a runtime class library. This is the library which provides the standard packages and classes needed by any Java program such as java.lang.Object and java.lang.String. RoboVM takes its runtime class library from the Android open-source project and all non-Android specific packages have been ported over to RoboVM. This means that any Java or JVM language code that only uses classes in the standard packages provided on Android should work the same under RoboVM.
Current status
RoboVM is still a work in progress but already quite usable. Version 1.0 is scheduled to be released before the end of 2014.
There are already at least 50 apps in the App Store based on RoboVM. For an up to date list of known apps see this.
Around 50% of the iOS APIs are covered so far and can be used from within a RoboVM iOS app. The RoboVM wiki has a list of the current status of these bindings.
RoboVM is at this time known to be capable of running code written in Scala, Clojure and Kotlin.
RoboVM is currently poorly documented. The 1.0 release which is due later in 2014 will be fully documented.
There is currently no support for debugging RoboVM apps. This will be addressed later this year.
Limitations
RoboVM is restricted to loading classes which have been ahead-of-time compiled into the app. This means that bytecode cannot be created dynamically at runtime and loaded into the app using a custom classloader. This rules out using technologies which creates or modifies classes at runtime.
Further information
- The RoboVM web site has some more information about the project.
- The source code is located on GitHub.
- Apple’s Provisioning Your iOS Device for Development document describes how to setup a test device.
- Apple’s App Distribution Guide has more information about how to submit apps to the App Store.
Creating Android apps with Xtend
What is Xtend?
Xtend 1 is a statically typed programming language that compiles to readable Java source code. The language itself is a best of breed design with special focus on readability and powerful extendability but makes Java interoperability a no brainer. It advocates a functional programming style and features things like multiple dispatch, extension methods, lambda expressions and even compile-time macros. Unlike other Java-alternatives Xtend doesn’t come with its own huge standard library, but only adds a couple of extension methods to the standard JDK. Xtend also guarantees the absence of Java-interoperability problems and comes with excellent IDE support.
Why Java on Android sucks
Java code tends to be very verbose, especially on Android. The Android APIs are very low level and often insufficiently typed (int everywhere). Another annoyance is the ubiquitous use of XML files and their bindings. Java 8 doesn’t work on Android, so we have to read through anonymous classes all over the place. Unfortunately, Java just doesn’t allow to trim the code down to a readable essence, but forces us to clutter it with superfluous symbols, type information and boilerplate idioms.
Minimum Requirements for JVM Languages on Android
A Java alternative on Android must not add any runtime overhead, which basically excludes all kinds of dynamic languages. In addition you don’t want to have any kind of unnecessary indirections for converting types. E.g. the code should only use stock Java and Android types and shouldn’t require back and forth conversion to gap interoperability issues. This is not only a performance problem but also very annoying during debugging. Finally Android limits the number of methods per application to 65536. So if you think about using an alternative to Java you don’t want to add a large standard SDK to your apps, as this will decrease the number of useful methods significantly. The Groovy SDK for instance would add 8000 methods already.
Xtend - A Perfect Fit For Android?
Xtend translates to idiomatic Java source code and relies almost totally on JDK and Android classes. There is no indirection, conversion or whatsoever going on at runtime. This means that Xtend code runs as fast as Java source code. A reduced runtime lib for Android is available, that is only 275kb small and includes everything you need. The Eclipse Plug-in integrates nicely with the ADT (Android Development Tools) and there is even a Gradle plugin 2 that works well with Android’s new build system 3. So let’s see how Xtend can improve a typical Android code base.
Hello Android!
As always we start by looking at a simple example program - Hello World:
class HelloWorldActivity extends Activity { override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState) val button = new Button(this) button.text = "Say Hello!" button.onClickListener = [ Toast.makeText(context, "Hello Android from Xtend!", Toast.LENGTH_LONG).show ] val layout = new LinearLayout(this) layout.gravity = Gravity.CENTER layout.addView(button) contentView = layout } }
On a first glance it should look pretty familiar to Java developers, as the example uses a javaish programming style. Also note that we use 100% Android SDK and JDK APIs only.
The main differences are:
- no semicolons (they are optional)
- property access to call setters and getters
- proper default visibilities (e.g. classes are public by default)
- lambda expressions instead of anonymous classes.
There’s much more to discover but before digging deeper into language features, lets see how to integrate the Xtend compiler in a proper Android build.
Building With Gradle
Xtend comes with plug-ins for all three commonly used build systems: Maven, Gradle and Ant. Google has recently introduced the new build system for Android projects, which is based on Gradle, so let’s see what is needed to build our “Hello World” project with it.
It is assumed that you have installed a recent version of Gradle and the Android SDK on your system and that you have properly set the ANDROID_HOME environment variable. You should also have added Gradle’s /bin to your PATH variable.
The build script ‘build.gradle’ should be put into the root folder of your Eclipse Android project. It looks like this:
buildscript { repositories { mavenCentral() } dependencies { classpath 'com.android.tools.build:gradle:0.8.+' classpath 'org.xtend:xtend-gradle-plugin:0.1.+' } } apply plugin: 'android' apply plugin: 'xtend-android' repositories { mavenCentral() } dependencies { compile ('org.eclipse.xtend:org.eclipse.xtend.lib:2.6.+') } android { compileSdkVersion 19 buildToolsVersion "19.1.0" sourceSets { main { manifest { srcFile 'AndroidManifest.xml' } java { srcDir 'src' } res { srcDir 'res' } assets { srcDir 'assets' } resources { srcDir 'src' } aidl { srcDir 'src' } } } }
It mainly imports the Maven and Xtend build plug-in and calls them. In addition, we add the runtime library and tell the android plug-in that we are using an Eclipse project-layout. With that in place go to the root folder of your project on the command line and run ‘gradle build’. It will do the rest.
More On Xtend
Despite the syntactic sugar, Xtend comes with some very useful language features such as operator overloading, template expressions and a powerful switch expression. By combining different features one can create new features. If you for instance need to build a dynamic UI you cannot use static XML but need to write the UI declaratively. Xtend allows you to use a builder syntax. This is what the simple “Hello World” UI could look like:
import static extension com.example.helloworld.UiBuilder.* class HelloWorldActivity extends Activity { override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState) contentView = linearLayout [ gravity = Gravity.CENTER addButton("Say Hello!") [ onClickListener = [ Toast.makeText(context, "Hello Android from Xtend!", Toast.LENGTH_LONG).show ] ] ] } }
The two methods linearLayout(Context ctx, (LinearLayout)=>void initializer) and button(ViewGroup group, String name, (Button)=>void initializer) are imported as extensions on Activity. They take a lambda (block of code in squared brackets) as an argument. The single parameter passed into those lambdas is called the implicit it, which similarly to this doesn’t need to be dereferenced explicitly. As you see a combination of lambdas, extension methods and the use of the implicit it makes for a very nice builder syntax. Many other nice looking APIs can be built with Xtend that allow for expressing your code in a readable and declarative way.
Say Hello From XML Hell!
The day to day job of an Android developer involves a lot of configuration and development in several XML files, being it resources for internationalized strings or simply the declaration of views. On Android it is advised to use XML, as there are proper solutions for overcoming the large device and SDK fragmentation that this platform has to deal with. However at the end of the day an application is not only made up of static views and data. We developers need to bind all of that stuff together and put some life into it. On Android you do that by using the R-class. It is a generated class containing int-constants for the various elements declared in XML files. Imagine the following two elements declared in a view XML, where the Button should be clicked in order to update the message in the TextView:
<TextView android:id="@+id/message_view" android:layout_weight="1" android:layout_width="0dp" android:layout_height="wrap_content" android:hint="@string/empty" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="sayHello" android:text="@string/hello_world" > </Button>
The typical Android-way would be to use the generated constants from the R class to get a handle of the TextView and implement a method for the onClick callback called “sayHello”:
class HelloWorldActivity extends Activity { TextView messageView override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState) // set the view using the int constant contentView = R.layout.main // get a handle on the TextView messageView = findViewById(R.id.message_view) as TextView } /** * Callback automagically called by Android */ def void sayHello(View v) { messageView.text = "Hello Android from Xtend!" } }
This is typical Android code including unsafe casts and naming conventions as well as a lot of boilerplate. With Xtend we can do much better.
Hello Xtendroid!
Xtendroid 4 is a small project that adds some library and so called active annotations specifically for Android development. An active annotation is basically a compile-time macro, that allows to participate in the compilation from Xtend to Java. You can freely mutate the annotated classes, generate additional types or read and write plain text files using this hook.
So what if we had an annotation that knows which view we want to bind to and that can generate the boilerplate for us? Moreover it could provide us type safe accessors to elements and callback methods. Here’s above’s activity using the @AndroidActivity annotation from Xtendroid.
@AndroidActivity(R.layout.main) class HelloWorldActivity { /** * Type safe callback */ override void sayHello(View v) { messageView.text = "Hello Android from Xtend!" } }
Now the activity only contains the behavior we wanted to add. All the plumbing for binding and the other boilerplate like setting the content view or extending Activity is done automatically. Also note that everything is now type safe and the IDE knows what’s going on, i.e. you get proper completion proposals etc..
Xtendroid has additional goodies for working with JSON objects, resource files or SQLite databases. But note that active annotations are just library, so you can easily build your own or customize existing ones as it seems fit.
If you want to try it out yourself, just download an Eclipse from 1 and install ADT using the update site 5. The Xtendroid project contains many examples including the ones we saw in this article. Have fun!
1 Eclipse Xtend
2 Xtend Gradle Plug-in
3 Android Gradle Plug-in
4 Xtendroid
5 ADT update site
About the Authors
Niklas Therning is the founder of the RoboVM open-source project and co-founder of Trillian Mobile AB - the main contributor to the RoboVM project. He has made it his mission to bring Java to iOS and do it properly. Before starting RoboVM Niklas co-founded the SpamDrain anti-spam service and worked as a contractor, mostly doing Java EE and web app development. Niklas holds a Master of Science degree in Computer Science from Chalmers University of Technology, Gothenburg, Sweden. Twitter @robovm.
Sven Efftinge is a passionate software developer who loves kite surfing, music and good food. He's the project lead of Xtext, a framework for developing programming languages and domain-specific languages and Eclipse Xtend, a statically-typed programming language for the JVM. Sven leads a research department for itemis in Kiel.
In the past years, mobile applications took the world by storm and already changed the way we use the internet for work or leisure. Various technologies emerged to create mobile apps and development processes start to consider mobile as first class citizens. But even though mobile already seems to be omnipresent, the future is just about to start. We're facing new generations of mobile devices like wearables or lots of mobile gadgets that make up the Internet of Things. We will be confronted with new types of user interfaces for displaying data as well as accepting commands. And we will recognize more and more companies going real mobile first. All this will influence the way we design, develop and test software in the coming years.
This InfoQ article is part of a series on the fast-changing world of Mobile technology. You can subscribe to notifications about new articles in the series here.