Since I recently ordered a HTC G1 smartphone that runs Android, I wanted to be able to code for it as well. But since I’m a bit allergic to Java, I decided to figure out how to use the nice language Scala on it.
This turned out not to be very hard, but it was frustrating enough that I shall explain how I think to best do it. (Please notice that I don’t really know Java, barely know Scala, had no clue about Android, and generally avoid “the Java way”, so please bear with me, and report any mistakes I did.)
In theory, we can just use scalac to compile our .scala files to
ordinary JVM .class-files and they would interoperate without
problems. In practice, this approach should be avoided, since
Android does things a little bit differently: it does not run Java byte
code, but
Dalvik byte code,
and you need to convert the .class files to .dex files to make them run on
the phone, a task I call dexing.
So you can try to dex your .class files together with
scala-library.jar, and after figuring how to give dx (the dexing
tool) more heap space (dx -JXmx512m), it will happily munch that,
think a rather long time, and spit out a not-very-compact ~800kb
Android .apk application that does nothing yet.
After doing that a dozen times, you run out of coffee. And swap,
maybe. Now, don’t try to “pre-dex” the scala libraries, because in a
.dex, everything is mangled into one file, and there are no tools
to combine these files.
Instead, you should “treeshake” the application such that only the required Scala classes end up in the Android binary. I’ll use ProGuard for this.
To make things short, here is a step-by-step guide:
Create the application skeleton (I use the Android SDK 1.1r1 for OS X):
% activitycreator --out helloscala com.example.helloscala.HelloScala % cd helloscalaDownload Scala (I used version 2.7.3) and ProGuard (I used version 4.3), and extract them somewhere (
$SCALA,$PROGUARD).Import the tools:
% mkdir tools % cp $SCALA/lib/scala-{compiler,library}.jar tools % cp $PROGUARD/lib/proguard.jar toolsModify the Ant file. For your reference, this is the modified build.xml.
Add inside the
compiletask:<taskdef resource="scala/tools/ant/antlib.xml" classpath="tools/scala-compiler.jar:tools/scala-library.jar" /> <scalac force="changed" deprecation="on" srcdir="${srcdir}" includes="**/*.scala" destdir="${outdir-classes}"> <classpath> <pathelement location="${android-jar}"/> <fileset dir="tools" includes="*.jar"/> </classpath> </scalac>Modify the
dextask to look like this (“proguard” as dependency, changed fileset)<target name="dex" depends="proguard"> <apply executable="${dx}" failonerror="true" parallel="true"> <arg value="--dex" /> <arg value="--output=${intermediate-dex-ospath}" /> <fileset dir="${outdir}" includes="*.min.jar"/> </apply> </target>Add the
proguardtask:<target name="proguard" depends="compile"> <taskdef resource="proguard/ant/task.properties" classpath="tools/proguard.jar" /> <proguard> -injars ${outdir}/classes:tools/scala-library.jar(!META-INF/MANIFEST.MF,!library.properties) -outjars ${outdir}/classes.min.jar -libraryjars ${android-jar} -dontwarn -dontoptimize -dontobfuscate -keep public class * extends android.app.Activity </proguard> </target>Now, rewrite the
.javafile in Scala (don’t forget to remove theHelloScala.javafile):package com.example.helloscala // we need these _root_ because com.android exists, // and we are in com.example.helloscala. import _root_.android.app.Activity import _root_.android.os.Bundle class HelloScala extends Activity { override def onCreate(savedInstanceState: Bundle) { super.onCreate(savedInstanceState) // use a bit of Scala's stdlib, just to show off setContentView(List(R.layout.main) first) } }
Done! Now you can build it by running ant.
If the build succeeded, check bin/HelloScala-debug.apk:
% du bin/HelloScala-debug.apk
20 bin/HelloScala-debug.apk
Nice and small (well, a pure-Java app only has 4k, but its a lot better than 800kb unstripped Scala standard library)! You can check which Scala libraries this uses with:
% jar tvf bin/classes.min.jar
Now start up the emulator, and when it has booted, install your application:
% adb install -r bin/HelloScala-debug.apk
Then, you can select it in the emulator, and you should see the default “hello world” screen.
Congratulations, you are running Scala on Android!
NP: Love—Laughing Stock