So Android development goes like this:
- You write Java code.
- The Java source is compiled into Java bytecode (.class files).
- The Java bytecode is translated into a Dalvik bytecode using the dx tool - a tool that comes with the Android SDK. (This tool is external to Android - it doesn't run inside the Android but in the development environment.)
- The Dalvik bytecode is executed by the Dalvik VM on the Android.
This is a major limitation in practice, and I found it when I was following Karl's steps to get Felix running on Android. I figured there had to be a way around this.
I started with the dx tool, since it's the only code I know of that's capable of doing the translation. It turns out that the tool's executable is just a thin shell script around a jar file (<android root>/tools/lib/dx.jar)! The next steps were pretty obvious:
- Try to run the dx tool on itself (actually, on the dx.jar that implements its functionality).
- Push the translated dx.jar onto the Android emulator and see if it works.
After a bit of exploring of the classes inside the dx.jar I was able to come up with an implementation of a classloader (com.cloudsmith.android.runner.TranslatingClassLoader) capable of translating Java bytecode into the Dalvik bytecode on demand i.e. when a Java bytecode class needs to be loaded
You can find an Eclipse project containing the classloder and a simple test application in this SVN repository (note that you will need to have Buckminster installed in your Eclipse IDE for the project builds to work properly).
Since this worked, I decided to incorporate the concept into the Apache Felix classloader previously modified by Karl. You can find the project here (note that you will need Buckminster for this project as well). With the modification introduced by this project the Felix is capable of loading and executing unmodified (containing Java bytecode classes only) bundles.
This can be demonstrated by loading the telnetd bundle directly from an OBR repository:
C:\>cd C:\Workspaces\workspace-android\org.apache.felix.android
C:\Workspaces\workspace-android\org.apache.felix.android>deploy.bat
1220 KB/s (0 bytes in 878988.000s)
1593 KB/s (0 bytes in 51000.000s)
755 KB/s (0 bytes in 12090.000s)
1345 KB/s (0 bytes in 129143.000s)
3 KB/s (0 bytes in 3329.001s)
8 KB/s (0 bytes in 137.000s)
C:\Workspaces\workspace-android\org.apache.felix.android>adb shell# sh /data/felix/felix.sh
sh /data/felix/felix.sh
Welcome to Felix.
=================
DEBUG: WIRE: 1.0 -> org.osgi.service.packageadmin -> 0
DEBUG: WIRE: 1.0 -> org.osgi.framework -> 0
DEBUG: WIRE: 1.0 -> org.ungoverned.osgi.service.shell -> 1.0
DEBUG: WIRE: 1.0 -> org.apache.felix.shell -> 1.0
DEBUG: WIRE: 1.0 -> org.osgi.service.startlevel -> 0
DEBUG: WIRE: 2.0 -> org.osgi.framework -> 0
DEBUG: WIRE: 2.0 -> org.apache.felix.shell -> 1.0
DEBUG: WIRE: 3.0 -> org.osgi.framework -> 0
DEBUG: WIRE: 3.0 -> org.osgi.service.obr -> 3.0
-> DEBUG: WIRE: 3.0 -> org.apache.felix.shell -> 1.0
-> obr list
obr list
Apache Felix Bundle Repository (0.8.0.incubator)
Apache Felix Shell Service (0.8.0.incubator)
Apache Felix Shell TUI (0.8.0.incubator)
telnetd (1.0.0)
-> obr deploy telnetd
obr deploy telnetd
Target resource(s):
-------------------
telnetd (1.0.0)
Deploying...done.
-> ps
ps
START LEVEL 1
ID State Level Name
[ 0] [Active ] [ 0] System Bundle (1.1.0.SNAPSHOT)
[ 1] [Active ] [ 1] Apache Felix Shell Service (1.0.2)
[ 2] [Active ] [ 1] Apache Felix Shell TUI (1.1.0.SNAPSHOT)
[ 3] [Active ] [ 1] Apache Felix Bundle Repository (1.1.0.SNAPSHOT)
[ 5] [Installed ] [ 1] telnetd (1.0.0)
-> start 5
start 5
DEBUG: WIRE: 5.0 -> org.osgi.framework -> 0
DEBUG: WIRE: 5.0 -> org.ungoverned.osgi.service.shell -> 1.0
DEBUG: WIRE: 5.0 -> com.softsell.open.osgi.telnetd -> 5.0
DEBUG: WIRE: 5.0 -> dtw.telnetd -> 5.0
[12/Feb/2008:01:50:42 GMT] Listening to Port 6,623 with a connectivity queue size of 5.
-> [12/Feb/2008:01:51:44 GMT] connection #1 made.
While the outlined approach is a bit of hacking it is still a proof of concept. And I hope that once Google releases the complete source codes of the Andorid SDK it won't be necessary any more or at least it will be possible to simplify/optimize it substantially.