In this article we present a way to extend the boot class path of the Dalvik-VM with additional Java libraries. That possibility is particularly interesting when developing Android applications written in Scala since it solves the issue we described in the article "Shrinking the Scala library code".
Our solution is partly inspired from Brault's article "Les dessous d'Android", published in the September 2009 issue of the Linux Magazine. In particular Frédéric Brault describes how to modify the Android runtime environment so that it behaves more Unix-like. He also presents BusyBox, a comfortable shell toolbox available for various embedded devices and licensed under GPLv2.
Note: The Android emulator includes Toolbox, a light-weight shell toolbox (~75 KB), which provides 51 shell commands with basic functionality while BusyBox (~1 MB in size) provides a much richer set of commands (e.g. 323 commands in version 1.16.1).
Android smartphones also provide the BusyBox shell toolbox; for instance when clicking on the thumbnail on the right you can see a screen capture of an HTC Desire device.
ramdisk.img
We want to create custom ramdisk images (one per available target) with
pre-installed Scala libraries and to pass them as argument when launching
the Android emulator (using the -ramdisk <file>
option) .
We proceed as follows (the operations below are actually performed by
the shell script "bin/createramdisks
"):
We first need to split the Scala library (our script uses the environment
variable $SCALA_HOME
to find the scala-library.jar
file) into three smaller pieces since the dx
tool fails to
process a so large archive file (about 6 MB for
scala-library.jar
);
Note: The
dx
tool lets you generate Android bytecode from .class files. The tool converts target files and/or directories to Dalvik executable format (.dex) files, so that they can run in the Android environment.
scala-actors.jar
(containing package scala.actors
),
scala-collection.jar
(containing package
scala.collection
) and scala-library.jar
(containing the remaining packages) into .dex files;
Finally we create our custom ramdisk images which will include the following additions respectively modifications:
/bin/busybox
);
/data/framework/
contains
the Scala libraries;
.dex
libraries (modified init.rc
file).
The generated ramdisk.img
files are kept in the user directory
$ANDROID_SDK_HOME/.android/avd/
and can thus be
shared between all
Android projects; here is an example of our Android user environment:
$ ls $ANDROID_SDK_HOME/.android/avd/ API_7.avd API_8_Google.avd-custom API_7.avd-custom API_8_Google.ini API_7_Google.avd API_8.ini API_7_Google.avd-custom API_9.avd API_7_Google.ini API_9.avd-custom API_7.ini API_9_Google.avd API_8.avd API_9_Google.avd-custom API_8.avd-custom API_9_Google.ini API_8_Google.avd API_9.ini $ cat $ANDROID_SDK_HOME/.android/avd/API_8.avd/config.ini hw.lcd.density=160 sdcard.size=128M skin.name=HVGA skin.path=platforms/android-8/skins/HVGA image.sysdir.1=platforms/android-8/images/ $ ls -s $ANDROID_SDK_HOME/.android/avd/API_8.avd-custom total 777 777 ramdisk.img
Note: For convenience we provide the following four archives for Android developers working on the Windows platform (a Unix environment is required to generate the ramdisk images).
The Java system libraries are located in the following two directories
(which are respectively read-only and read-write directories); the jar
files contained in directory /data/framework/
are installed
once using the command push-jars
defined in the the Ant build
script build.xml:
$ adb shell /bin/ls -s /system/framework /data/framework /system/framework: 9 am.jar 2647 framework.jar 84 android.policy.jar 3 ime.jar 74 android.test.runner.jar 2 input.jar 6 bmgr.jar 26 javax.obex.jar 2105 core.jar 32 monkey.jar 229 ext.jar 11 pm.jar 4668 framework-res.apk 607 services.jar 9 framework-tests.jar 4 svc.jar /data/framework: 201 scala-actors.jar 1135 scala-library.jar 735 scala-collection.jar 312 scala-mutable.jar 380 scala-immutable.jar
We check that the value of the environment variable BOOTCLASSPATH
is correctly extended with the pre-installed Scala libraries.
$ adb shell echo '$BOOTCLASSPATH' BOOTCLASSPATH=/system/framework/core.jar:/system/framework/ext.jar:\ /system/framework/framework.jar:/system/framework/android.policy.jar:\ /system/framework/services.jar:/data/framework/scala-library.jar:\ /data/framework/scala-collection.jar:/data/framework/scala-immutable.jar:\ /data/framework/scala-mutable.jar:/data/framework/scala-actors.jar
And we have direct access to the BusyBox commands
(e.g. "/bin/ls
") thanks to the modified PATH
variable:
$ adb shell echo '$PATH' /bin:/sbin:/system/sbin:/system/bin:/system/xbin
Finally we briefly present a small shell script excerpt to demonstrate how
our custom ramdisk.img
files are easily selected before
launching the Android emulator with some user configured AVD:
## .. (skipped) AVD="API_9" # some default AVD AVD_HOME=$ANDROID_SDK_HOME/.android/avd if [ ! -f "$AVD_HOME/$AVD.ini" ] ; then echo "Error: Device '$AVD' is unknown." echo " We cannot execute $EMULATOR." exit 1 fi if [ -z "$ANDROID_EMULATOR_OPTS" ] ; then EMULATOR_OPTS="-no-boot-anim -no-skin" if [ -f "$AVD_HOME/$AVD.avd-custom/ramdisk.img" ] ; then RAMDISK=$AVD_HOME/$AVD.avd-custom/ramdisk.img EMULATOR_OPTS="$EMULATOR_OPTS -ramdisk $RAMDISK" fi else EMULATOR_OPTS="$ANDROID_EMULATOR_OPTS" fi exec $EMULATOR $EMULATOR_OPTS -avd $AVD
For convenience the .zip archive android-sdk.zip
includes the two shell scripts
bin/emulator
and bin/emulator-maps
which automatically check for the presence of custom
ramdisk.img
files (the scripts are also present in the .zip
archive unlocking-android.zip).
That's it ! We can now switch back to our Scala code writing.
Update (December 10, 2010): Linux users should first check the version of the GLIBC library installed on their system (eg. version 2.7 on Ubuntu 8.04 aka "Hardy") as the Android 2.3 emulator requires version 2.11 (or newer) of the GLIBC library (installation proceeds silently :-( You are now warned!):
/opt/android-sdk-linux_86/tools$./emulator ./emulator: /lib/tls/i686/cmov/libc.so.6: version `GLIBC_2.8' not found (required by ./emulator) ./emulator: /lib/tls/i686/cmov/libc.so.6: version `GLIBC_2.11' not found (required by ./emulator)