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 helloscala
Download 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 tools
Modify the Ant file. For your reference, this is the modified build.xml.
Add inside the
compile
task:<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
dex
task 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
proguard
task:<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
.java
file in Scala (don’t forget to remove theHelloScala.java
file):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