Creating Android applications with Clojure, Part 1
Over the past few weeks, I have started doing some application development on the Android platform. Overall, it has been a pleasant experience. The documentation, while not the best, is fairly good. Being an experienced Java programmer, I was comfortable enough to start writing real code quickly.
Despite all of this, writing in Java just is not as much fun any more. There is too much boilerplate code, and the language is missing some key features, such as closures. So, I decided to look into developing with some alternate JVM languages such as Scala and Clojure
In this two-part series of posts, I will discuss my experiences with coding in Clojure for Android in an effort to document what has worked for me. First, I will concentrate on those issues that affect the user’s experience. In the next part I will write about the development process.
Loading, please wait…
By far, the biggest problem with a Clojure application on Android is the load time. On a device, it take at least five to ten seconds for an activity written in Clojure to load up. This may be acceptable for some long-running applications, such as a game, but it is likely to be frustratingly slow for many other applications.
Investigating the problem
Profiling an application during this long wait time reveals that it is due to
to the loading of the class clojure.lang.RT
. One of the primary functions of
this class is to bootstrap the Clojure runtime so that all of the basic
language functionality is available for your program.
Unfortunately, for a constrained environment such as a mobile phone, this
simply takes too long. In fact, it is loading the clojure.core
name space
that is root of the problem, and some of that work is unnecessary.
Slimming down Clojure
The most direct way to solve this problem is to have less of the language to
load. In fact, there are some parts of Clojure that you simply cannot use on
an Android device, such as dynamic code generation and compilation, e.g.
gen-class
or defrecord
. You can AOT compile code that uses these forms,
but you cannot generate new types within your application at runtime.
Perhaps the most direct way to help resolve the loading issue to create a
modified version of Clojure that omits some of this functionality, a sort of
Mini-Clojure. Perhaps a better solution may be to somehow make it so that at
start-up only a minimal set of functionality is available and the rest loads up
either in the background or on demand. Either way, slimming clojure.core
is
not a trivial endeavour.
Bootstrapping your application
Presuming that you are not going to hack Clojure for a quick start-up time, how
do you create a better user experience during start-up? Since the problem is
caused by loading the clojure.lang.RT
class, the key is to choose when and
how that happens.
If you code your main activity in Clojure, this means that loading Clojure will happen before anything is displayed to the user. Ideally, what would be better is to show a splash screen or progress dialog while Clojure loads in the background.
The easiest way to do this is ensure that your initial activity is written in
Java. Using this approach, the onCreate
method of your activity launches
another thread that will load Clojure in the background. Once it is loaded, it
can call back to your activity in the GUI thread so that your application can
continue. At this point, you have two options:
- Integrate your Java-based activity with your Clojure code by directing all of the various call-back methods to your Clojure code.
- Launch a second activity that is pure Clojure.
I have had success with the first approach. Once you get passed the long initial wait, the rest of the application seems to run as well as a Java-based program. I have not tried the second approach, but I do not see why it should not work.
I believe a long start-up time is the biggest obstacle to a user-friendly Clojure program on Android. However, by loading Clojure in the background, it is possible to distract the user with a progress bar, splash screen, or animation.
Application size
The second major problem for users is the size of a Clojure-based application. Most users will be running your program on a mobile where disk space is limited, RAM is rare, and bandwidth is expensive.
A minimal Clojure application including the full Clojure libraries will be about one megabyte to download. This may not seem too bad as far as applications go, but it is enormous compared to the minimal Java application of about twenty kilobytes.
Once downloaded, the platform will uncompress the minimal Clojure program to
about four megabytes. Again, not too bad, but still relatively large given
that it does nothing. In my experiments, deleting some of the unused libraries
such as clojure.test
and clojure.java.javadoc
helps save some space, but
not significantly.
It is hard to calculate exactly how much memory use can be attributed to Clojure, but my informal experiments indicate that it may be about 1.5 megabytes. It has certainly been my experience that small Clojure programs use much more memory than comparable Java programs. On Android, a small memory footprint is quite important as you do not want to have your program killed to free up memory. If your application is killed, the user will have to endure the long start-up time once more.
Final thoughts
I am certainly happy to note that you can do Android development in Clojure, but the current standard Clojure library for the JVM does not make it easy. The good news is that good Android support is a goal for the Clojure development team. I believe that given some attention, it is quite feasible to develop a stream-lined Clojure library suitable for the Android platform1.
Nonetheless, it is possible to use the above bootstrapping technique to create a user experience better than a ‘black screen of death’. Given your particular application, it may even be perfectly acceptable.
Coming in part two
In part two, I will shift focus away from the user to describe how the development process works with Clojure. In particular, I will examine how the inability to dynamically generate code on the platform impacts development and some techniques for working around the problem.
Footnotes
TrackBacks
No trackbacks, yet.
Trackbacks are closed for this story.
Comments
-
On Friday, 4 Feb 2011 02:57, Iouri Goussev wrote the following:
Hmm, yes it is interesting why clojure can not generate code at run-time? It is a dalvik restriction?
-
On Friday, 4 Feb 2011 08:59, Daniel Solano Gómez wrote the following:
I am going to write a bit more about that in part two, but it essentially boils down to the fact that Clojure produces JVM bytecodes instead of Dalvik bytecodes.
-
On Friday, 4 Feb 2011 12:17, Scott wrote the following:
I think you can get faster start times using ProGuard, see https://github.com/hsaliak/android-clojure-flashlight-example at the bottom of the README he has timings saying it starts in 2s on his phone.
-
On Friday, 4 Feb 2011 13:00, Mike wrote the following:
Sounds like you need one of the old LISP tree-shakers, which would "shake" the dependency tree to get rid of anything in the standard library that wasn't used by your initial source files.
-
On Friday, 4 Feb 2011 13:25, Daniel Solano Gómez wrote the following:
Mike and Scott:
Thank you for your comments. I have used ProGuard to help with Scala development on Android, and I plan on writing about using it with Clojure in part two. I will also include some code examples to help demonstrate this and other techniques.
-
On Sunday, 9 Sep 2012 02:00, Jeff wrote the following:
i guess "Coming in Part 2" article is really the "Building with Ant" article? (http://www.deepbluelambda.org/programming/clojure/creating-android-applications-with-clojure--building-with-ant)?
-
On Sunday, 9 Sep 2012 22:08, Daniel Solano Gómez wrote the following:
Jeff:
Yes, that would be it. And part 3 would be Creating Android applications with Clojure: Slimming things down with roGuard. However, I would recommend you look at lein-droid for Clojure/Android development.
Comments are closed for this story.