28

My understanding is that Google didn't like Oracle's licensing policy for using the JRE in Java ME so it just rewrote it using its own JVM specification that mimics the JRE but behaves a little bit differently, especially when it comes to making things more efficient and more secure.

So, if my understanding is correct, it means that when javac is ran on some Java source code and compiled into "binary" byetcode, a compliant JVM will interpret that bytecode different than Dalvik will (in some cases). This is the inherent difference between Dalvik and other (compliant) JVMs.

If anything I have said so far is incorrect, please begin by correcting me!

Now, if Android came with its own compiler (which it might), and compiled Java source in a different (Dalvik-compliant) manner than javac, then I could understand how some code (not compiled with the Android SDK) would not run on an Android device:

MySource.java --> javac --> MySource.class (JRE-compliant) --> JVM --> running Java app
MySource.java --> android-compiler --> MySource.class (Dalvik-compliant) --> Dalvik JVM --> running Android app

However, it looks like you use javac to compile Android apps!?!? So it looks like we have this:

MySource.java --> javac --> MySource.class (JRE-compliant) --> JVM --> running Java app
MySource.java --> javac --> MySource.class (JRE-compliant) --> Dalvik JVM --> running Android app (???)

If javac is used to compile all sources into bytecode, then why is it that Dalvik can't run some types of Java code?

I asked a very similar question yesterday and although it was technically answered (after re-reading my question I see I was simply not specific enough) no one was able to explain what it is that's inherent to Dalvik that makes it impossible to run Java code from projects like Google Guice or Apache Camel. I was told that in order to get Camel to run on Dalvik, that I would have to get Camel's source and then it would have to be "built with the Android SDK", but I couldn't get clarity on what that meant or implied.

With Camel, for instance, you have this (simplified):

RouteBuilder.java --> javac --> RouteBuilder.class --> jartool --> camel-context-2.9.jar --> JVM --> running Camel ESB
RouteBuilder.java --> javac --> RouteBuilder.class --> jartool --> camel-context-2.9.jar --> Dalvik JVM --> doesn't work !!! (???)

Clearly, something is happening inside the Dalvik JVM that prevents it from running certain types of Java code. I'm trying to understand what types of Java code will not run when "fed" into the Dalvik JVM.

Edit: In before "but Camel 3.0 will run on Android!" I know - not my question!

Ben ODay
  • 20,784
  • 9
  • 45
  • 68
IAmYourFaja
  • 55,468
  • 181
  • 466
  • 756

3 Answers3

30

I'm trying to understand what types of Java code will not run when "fed" into the Dalvik JVM.

Dalvik JVM differs from other JVMs in following aspects:

  • It uses special DEX format for storing applications binaries vs. JAR and Pack200 formats used by standard Java virtual machines. Google claims that DEX results in smaller binaries than JAR. I think they could use Pack200 with the same success, but they decided to go their own way in this aspect

  • Dalvik JVM was optimized for running multiple JVM processes simultaneously

  • Dalvik JVM uses register-based architecture vs. stack based architecture of other JVMs with intent to speed up execution and to reduce binary sizes

  • It uses its own instructions set (not a standard JVM bytecode)

  • One can run (if needed) several independent Android applications within a single JVM process

  • Application execution can span across several Dalvik JVM processes “naturally”. To support this it adds:

  • Special object serialization mechanism based on Parcel and Parcelable classes. Functionally it serves the same purpose as standard Java Serializable, but results in smaller data footprint and is potentially more lenient towards differences in versions of classes

  • Special Android way to execute inter process calls (IPC) based on Android Interface Definition Language (AIDL)

  • Until Android 2.2 Dalvik JVM did not support JIT compilation which adversely impacted Android application performance. Adding it in 2.2 improves markedly execution speed for often used applications

Arlen Beiler
  • 15,336
  • 34
  • 92
  • 135
user370305
  • 108,599
  • 23
  • 164
  • 151
  • Thanks @user370305 (+1) - your answer is great at comparing the differences between the Dalvik JVM and a "Java compliant" JVM, however it still does not explain **the specific reason** why I can have a Java project that contains my own code and Apache Camel dependencies and have it run fine on a normal JVM, but Dalvik can *only run my code*. **Is it because my code would get cross-compiled by `dx` and that Apache Camel has already been compiled (by something othher than `dx`)?** If that's the case, then finally, I "get it". Thanks again! – IAmYourFaja Jul 07 '12 at 11:35
  • 1
    For Android Programs are commonly written in Java and compiled to bytecode. They are then converted from Java Virtual Machine-compatible .class files to Dalvik-compatible .dex (Dalvik Executable) files before installation on a device. – user370305 Jul 07 '12 at 11:38
  • 2
    Also... dx is used to convert some (but not all) Java .class files into the .dex format. Multiple classes are included in a single .dex file. Duplicate strings and other constants used in multiple class files are included only once in the .dex output to conserve space. Java bytecode is also converted into an alternative instruction set used by the Dalvik VM. An uncompressed .dex file is typically a few percent smaller in size than a compressed .jar (Java Archive) derived from the same .class files. – user370305 Jul 07 '12 at 11:41
  • The Dalvik executables may be modified again when installed onto a mobile device. In order to gain further optimizations, byte order may be swapped in certain data, simple data structures and function libraries may be linked inline, and empty class objects may be short-circuited. – user370305 Jul 07 '12 at 11:42
  • 2
    Re: using pack200 -- this would only save storage space, it would still need to be loaded into memory in uncompressed form. Dex files are smaller than a set of uncompressed .class files, so they take up less memory when loaded. – JesusFreke Jul 07 '12 at 19:55
  • @user370305, "One can run (if needed) several independent Android applications within a single JVM process" How? Can you give any reference? – Wu Yongzheng Jan 26 '14 at 07:30
  • BTW Binder has got nothing to do with Dalvik JVM, Binder just provides an IPC mechanism which is independent of the Dalvik VM (and the now Android Runtime - ART). Same with the Parcel and Parcelable. – Tuxdude Aug 22 '15 at 07:23
25

If anything I have said so far is incorrect, please begin by correcting me!

Ummm, well...

  • The Dalvik VM has technical advantages over the Java VM for mobile environments, most notably aggressive use of copy-on-write memory sharing, so the entire VM and standard class library is shared among all Android SDK app processes, reducing the net per-process memory footprint. See user370305's answer (posted while I was wrapping this up) for more.

  • The bytecode from javac is cross-compiled into Dalvik bytecode as part of the Android application build process. The Java VM cannot execute Dalvik bytecode any more than it can execute the output of /dev/random; similarly, the Dalvik VM cannot execute Java bytecode.

Here is a blog post of mine from around two years ago that goes into additional points.

If javac is used to compile all sources into bytecode, then why is it that Dalvik can't run some types of Java code?

Because the javac bytecode output is cross-compiled. The cross-compiler (dx) handles a very specific flavor of javac output, meaning that while it works with the classic javac (what you would have gotten from java.sun.com) and OpenJDK for Java 1.5 and 1.6, it will not work with alternative compilers (e.g., GCJ) and, at minimum, will not work with any new bytecodes from Java 7.

no one was able to explain what it is that's inherent to Dalvik that makes it impossible to run Java code from projects like Google Guice or Apache Camel

Personally, I have never used Google Guice, though Roboguice works on Android. I had never heard of Apache Camel prior to your question and am rather confused to find that it is not a Java port of Perl. :-)

Any tools that do runtime JVM bytecode generation will not work on Android, simply because the cross-compiler is only available at compile-time, not run-time. Also, I am unfamiliar with the techniques used by runtime JVM bytecode-generating tools and how they get the JVM to execute that bytecode, and therefore I do not know if equivalent hooks exist in Android to have Dalvik run arbitrary chunks of Dalvik bytecode.

However, since you declined to specify exactly what "Java code from projects like Google Guice or Apache Camel" you are having problems with, and since I am not intimately familiar with those projects, it is difficult to comment further.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • 1
    Thanks @CommonsWare (+1) - so I was under the assumption I could just get Camel's source code and re-compile it using `dx` and make it Android-friendly. But then I read your statement "*Any tools that do runtime JVM bytecode generation will not work on Android...*" So I assume, since Camel does bytecode weaving, etc., that this is strategy is out of the question? I assume that the minute Camel tries to do bytecode weaving it will do so with the normal/standard/compliant JVM instruction set and either kill Android or have it behave completely unexpected? Am I on track or off base? Thanks again! – IAmYourFaja Jul 07 '12 at 11:39
  • 1
    +1. Very interesting. Does cross-compilation means the the bytecode generated by javac goes through a second-pass compilation phase transforming it into Dalvik byte-code? – JB Nizet Jul 07 '12 at 11:39
  • 1
    @JBNizet - Yes, look at my comments below my answer. – user370305 Jul 07 '12 at 11:49
  • @AdamTannon: "So I assume, since Camel does bytecode weaving, etc., that this is strategy is out of the question?" -- I know of nobody who has worked out the techniques of doing runtime bytecode generation on Android. I cannot say that it is impossible, as that is outside of my area of expertise. "I assume that the minute Camel tries to do bytecode weaving it will do so with the normal/standard/compliant JVM instruction set and either kill Android or have it behave completely unexpected?" -- well, it does not appear to rupture the space/time continuum, but it is unlikely to work. :-) – CommonsWare Jul 07 '12 at 11:56
  • 8
    @CommonsWare - DexMaker is a tool for runtime bytecode generation. Although it is not a drop-in replacement, so to use anything that does dynamic bytecode generation on Android, you would first have to port that tool to use DexMaker. – JesusFreke Jul 07 '12 at 20:00
  • 1
    @AdamTannon: The APIs that these tools use to do dynamic bytecode generation just aren't there on Android. So dalvik will simply fail with some type of ClassNotFound exception if you tried to run one on Android – JesusFreke Jul 07 '12 at 20:01
  • @CommonsWare For an interesting example that uses DexMaker, see http://code.google.com/p/image-playground/ . – danfuzz Jul 07 '12 at 20:50
  • @CommonsWare Re: "it will not work with alternative compilers (e.g., GCJ)" I'd say it won't *necessarily* work with any given compiler, but that said there is plenty of "alternative" compiler output that dx handles just fine. For example, I actually spent a fair amount of time getting some surprising-to-me edge cases ironed out, which were found in the output of `scalac`. – danfuzz Jul 07 '12 at 21:04
  • +1 Great and detailed explanation. To me personally what answered the question was "The cross-compiler (dx) handles a very specific flavor of javac output". I just don't get why then that specific javac is not a part of dx tool or at least not included in the developer bundle since using anything else will not work? – Igor Čordaš Feb 13 '14 at 15:21
  • @PSIXO: `javac` doesn't really exist in a vacuum; I presume that it needs more of the JDK to actually do its work. Also, as danfuzz notes, other tools that generate JVM bytecode *can* work just fine with `dx`. Not all tools do. – CommonsWare Feb 13 '14 at 15:29
  • I "am rather confused to find that it is not a Java port of" *OCaml*, lol – Hongxu Chen Jan 23 '17 at 06:50
13

This picture from Android official document illustrate the build process of Android APK, it will help to understand the difference between java bytecode and dalvik executable. enter image description here

Here I give an example to demonstrate some of the differences.

Hello.java

import java.io.*;
public class Hello {
    public static void main(String[] args) {
        System.out.println("hello world!!!!");
    }
}

use javac to compile Hello.java to java bytecode Hello.class

$ javac Hello.java

Then use dx tool from android sdk convert java bytecode Hello.class to Hello.dex

$ $ANDROID_SDK_ROOT/build-tools/21.1.2/dx --dex --output=Hello.dex Hello.class

After that, use adb to put Hello.class and Hello.dex to Android device or emulator.

$ adb push Hello.class /data/local/tmp/
$ adb push Hello.dex /data/local/tmp/

use adb shell to enter the shell environment of Android device. Then use the command /system/bin/dalvikvm to execute the simple java program we just created Hello.class and Hello.dex

$ dalvikvm -Djava.class.path=./Hello.class Hello
java.lang.NoClassDefFoundError: Hello
    at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.ClassNotFoundException: Didn't find class "Hello" on path: ./Hello.class
    at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:65)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:501)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:461)
    ... 1 mor
$ dalvikvm -Djava.class.path=./Hello.dex Hello   
hello world!!!!

In the example above, when we use the java bytecode Hello.class, dalvikvm complaint error, if we changed the class to dalvik executable Hello.dex, it would run properly.

alijandro
  • 11,627
  • 2
  • 58
  • 74