Logo Computer scientist,
engineer, and educator
• Software  • The KBOX3 project

Compiling and running Java code under KBOX3

KBOX logo KBOX3 has preliminary, experimental support for compiling and running (Android) Java code at the command prompt. This page describes in outline how to do this, and then how it works. Please note that I'm assuming a fairly high level of Java knowledge here: building Java on the command line isn't necessarily a job for the beginner even with a desktop computer, and Android introduces its own complexities. In any event, the basic process is this:

  • Compile the Java source files to .class files
  • Bundle the .class files into a JAR file (if there are more than one of them)
  • Convert the JAR file to an android DEX file
  • Run the main class from within the DEX file using the Dalvik JVM
The steps are almost identical to building and running Java code at the command line on a desktop computer, with the exception of the extra DEX step.

It's not clear how practical it will prove to be, to build Java applications on an Android device — there are quite a few limitations compared to desktop Java development. However, the utility BBCWeatherJ builds on the device and, although it's not a heavyweight application, it uses networking and XML parsing operations, so it's not Hello World, either.

Quick start — one Java source file

Install the java-support package (see the downloads page.
$ dpkg -i /path/to/java-support-0.0.1_kbox3.html
Create a Java source file using, e.g., Vim or nano. For this example, assume the file is Test.java, defining a Java class Test in the default package. Because we're going to run the class on the command line, it must define a main() method.

Compile the Java class in the usual way.

$ javac Test.java 
This should create Test.class in the same directory as the Java source file. Convert the class to Android DEX format:
$ dx --dex --output=test.dex Test.class 
Run the class:
$ dalvikvm -cp test.dex Test

Slightly slower start — multiple Java source files

In addition to java-support, you will also need a zip utility (because KBOX3 does not have a 'jar' utility yet). Get zip from the downloads page and install it:
$ dpkg -i /path/to/zip_3.0_kbox3.html
In this example, assume we have two Java files, Test.java and AnotherTest.java. They are in the package test, and the main() method is defined in Test.java.

Compile the classes, placing the compiled output in the out directory (this will keep things nicely bundled together for the next step):

$ javac -d out/ Test.java AnotherTest.java 
Note that a directory test is created under out, because the directory hierarchy must match the package hierarchy.

Create a JAR file containing the classes, maintaining the directory hierarchy. Note that a JAR file is structurally a zip file, so we can use the zip utility for this.

$ cd out
$ zip -r test.jar . 
$ cd ..
Convert the JAR file into an Android DEX file:
$ dx --dex --output=out/test.dex out/test.jar 
Then run the program:
$ dalvikvm -cp out/test.dex test.Test 
Note that the class name must be qualified by its package name — we run a class, not a file. Note that if you want to repeat these steps, you'll need to remove test.dex and test.jar from out/, else they will end up in the zip file in the next cycle; this will not hurt, but it will lead to unnecessary bloat.

Of course, these steps can readily be automated in a script or a Makefile.

How it works

Running Java on Android

The Android Java runtime can be invoked using the command dalvikvm. In fact, since Android 5 the Java runtime is no longer Dalvik, but the same name is used. dalvikvm takes similar command-line arguments to java, but takes as its input Android DEX files rather than .class files.

To run dalvikvm under KBOX3 it is necessary to make the directory /data/dalvik-cache available, otherwise the JVM will abort on startup. This directory is not normally visible in the KBOX filesystem, but can be linked in like this:

$ cd /data
$ ln -sf /android_root/data/dalvik-cache .
The java-support package takes care of this, among other things. With this done, it should be possible to run a Java class that is contained in a DEX archive like this:
$ dalvikvm -cp {DEX_file} {class_name} [options] 
This will run the main() method in the specified class just as java would.

Building Java on Android

Android does not provide a Java compiler. In fact, the Android SDK for desktop computer does not provide a Java compiler either — developers are expected to use the familiar Sun/Oracle JDK to generate Java .class files, and then use the dx utility to convert them to Android DEX format.

Happily, javac is largely written in Java. The implementation in a standard desktop JDK is mostly in the tools.jar bundle, which is now open-source (GPL v2). So all (!) that we need to do to get a Java compiler on Android is to build tools.jar using JDK1.6, and convert it to Android DEX format. It turns out that there's a bit more to it than this — limitations in the Dalvik JVM mean that quite a few code changes were necessary (see the Limitations section below for details). Building tools.jar itself is not enough, because the compiler needs access to all the Java classes referenced by the application being built, including the platform classes (in java.lang, etc.) These do exist on Android but — so far as I can tell — only in DEX format, which the compiler can't read. So we must also provide a JAR-based implementation of the complete Android API set. Happily, such a thing is provided — android.jar — in the Android SDK, for use with desktop build tools. In fact, several such things are provided — one for each Android API level. So to build a Java application under KBOX, you could copy a suitable android.jar to the device and do this:

$ dalvikvm -cp tools.dex com.sun.tools.javac.Main -cp /path/to/android.jar /path/to/java/code/*.java
Of course, the resulting class files must then be converted to DEX format using the dx utility.

Note that the command above has two -cp (class path) arguments: the first is to the Dalvik VM, indicating where the compiler JAR is; the second is to the Java compiler, indicating where it should look for framework classes.

To make this process less cumbersome, java-support contains a build of javac that automatically adds the the Android API JAR to the class search path, and a script to simplify its execution, so you should just be able to do

$ javac [options] java_files... 
in the usual, desktop way.

Assembling multiple Java classes into an application

In reality, a Java application is rarely implemented in a single class. The DEX format is an archive, as JAR is, and it seems as if the dx utility should be able to build a DEX file from a collection of named .class files. However, if that is possible, I've never been able to make it work. It is straightforward, however, to build a DEX archive from a JAR file. So to build an application from multiple Java classes, all we need do is package them into a JAR, and then feed the whole JAR into dx.

KBOX3 does not have a jar utility (yet) but this is not a problem — we can build a JAR archive using zip — the file formats and compression methods are identical. Some trouble has to be taken to ensure that the directory hierarchy in the ZIP/JAR are a perfect match for the package hierarchy in the Java code, but that's a problem when using jar on the command line as well.

Limitations

The methods described in this article exploit the built-in Dalvik/ART Java runtime in Android — it's not a new kind of Java. Dalvik offers a reduced API set compared to desktop/server Java, and some APIs are implemented differently than might be expected. A good example is the API for retrieving resource bundles from the JAR file that contains the running program — getResourceAsString() and the like. These methods have been implemented to search for resources in an APK file, not in the JAR file, to be more useful for the developers of Android GUI apps. These API calls are therefore completely useless for a console application, and have to be replaced with something else. To port the javac compiler, for example, I had to replace these resource API calls with functions that read message strings from a named file.

There is no problem in principle with integrating 3rd-party Java libraries with an Android Java program, and it could be very useful to do so. There are two pragmatic problems, however. First, Android Java is at present aligned to JDK1.6, and the dx tool can't handle code generated by later versions that this. But most open-source Java libraries are now built using Java 7 or Java 8, so they would need to be rebuilt. But the tools needed to rebuild them are themselves all based on Java 7 and Java 8, and you can see where this is leading...

Second, when most libraries are distributed in binary format — even if they were built with Java 6 — they are distributed as JAR files, so they will need to be converted to DEX format for Android. This is not necessarily a large hurdle, but it is at least an irritation.

Copyright © 1994-2015 Kevin Boone. Updated Jul 31 2015