Java Developer with 6+ years of experience. Database and ORMs specialist with a visible soft spot for Postgres. Right now in favor of backend, he makes web applications using SPRING. He took part in commercial projects for energetic and industrial sectors. Grzegorz secretly dreams about going back to his youthful love: developing his own MMO game. His second full-time job is raising a two-year-old little rascal, and the third one - building a house and struggling with construction business’ crafty fellows.
Several months ago, my adventure with creating mobile apps for Android began. In the previous article, I described my first observations concerning the differences I could see between the creation of back-end and Java mobile applications. Today, I’d like to tell you about the difference between .class files in Java and .dex files in Android. I’ll also show you how to create a .dex file containing a Java class step by step.
Why is this so important?
At first sight, it might seem that Android and Java are exactly the same, but when you delve deeper into them it turns out that Android is a completely distinct type of architecture, with different low-level operations, different file structures, and many others. And does the knowledge of those differences influence a programmer’s work? It certainly has a direct impact in Android, where you have to pay attention to the optimization of code during programming from the very beginning, whereas in large apps it is important to use external libraries in Android. In the article below I’m going to tell you why.
Each Java class is compiled to Java bytecode. In result, a .class file is created; Java bytecode which is a set of instructions for the Java virtual machine. There are 256 instructions: out of them, 198 are in fact used, 54 are reserved to be used in the future, and 3 are considered as permanently non-implemented. The instructions for JVM are commonly referred to as opcode (operation code). Each instruction takes 1 byte of memory, in special cases 2.
JVM vs. DVM
To let you better understand the differences between JVM (Java Virtual Machine) and DVM (Dalvik Virtual Machine) in Android, I will describe in a nutshell what happens after .java files are compiled to bytecode in JVM and DVM.
In JVM, .java class files are compiled to separate .class files, one file per each class. No matter whether the class is public, private, or static. During work, JVM dynamically reads in bytecode to each .class file in case the class is required.
Meanwhile in DVM, bytecode of all the classes is in one .dex file (Dalvik Executable). One of the most significant differences between JVM and DVM is that DVM does not use Java bytecode but it employs its own Dalvik bytecode, i.e. a set of instructions (opcode) destined for DVM.
One of the reasons why DVM uses Dalvik bytecode is because DVM, unlike JVM, is based on register, while JVM is based on stack. That is why most JVM opcode instructions refer to stack-based operations, and in DVM, most instructions are related to register-based operations.
So, how does DVM use Java classes added as .jar files to an Android app? It happens because all the .class files are recompiled by means of dx (Dexer) to Dalvik bytecode, and then they are attached to the .dex file. Dexer is a standard component of Android SDK tool kit.
How does it look in theory?
To see the differences between the .class and .dex files, first you need to take a look at the structure of both types of files. Below, I have deconstructed each of the above mentioned files. You can single out the files more distinctly, however, I have decided that such decomposition is enough.
Figure 1: The structure of a .class file
Descriptions of the most significant elements forming a .class file are as follow:
Constant Pool – this is the list of constants in the class, just like constant string fields, class, field, and interface names.
Access Flag – 2 bytes determining whether the file is a class or interface, and whether the class is final, protected, or public, etc.
This Class – current class name field.
SuperClass – 2 bytes indicating the superior class
Interfaces – all the interfaces implemented by a given class.
Field – field defined by the class file.
Method – methods defined by the class.
Attributes – a list of all the attributes of the class.
Figure 2: The structure of a .dex file
I am now going to present you the descriptions of the most significant elements forming a .dex file:
Header – contains the basic info about the file, its size, indicators to various elements of the file.
String_ids – a list of identifiers of all the strings used by the file.
Type_ids – a list of identifiers of all the types included in the file (classes, boards, primitives).
Proto_ids – a list of prototypes (structures) for file references.
Fields – a list of field identifiers referred to in the file.
Methods – a list of identifiers of all the methods included in the file.
Classes – consists of 8 parts: class id, access_flags, super class type_id, interface list address, source file name string_id, class data address, address of the data initializing the static fields, address of the related annotations to the class.
Data – a section of data; actually, previous .dex file fragments mostly refer to the addresses in this section.
How does it look in practice?
So much for theory. Now, basing on the example below, I will try to show you how to make four .class files referring to one class each out of one .java file containing four classes (public, private, protected, and static). Next, the compiled .class files will be recompiled to a .dex file.
I created four classes in one file: MyPublicClass.java
Then, I compiled the file to JVM bytecode using the following command: javac MyPublicClass.java
As a result, I got four .class files.
Let’s see what happens after you’ve compiled the same class to a .dex file in Android using Dexer. To create the .dex file, I will pack the previously created .class files to one .jar file. I will use the following command:
jar cvf MyPublicClass.jar com/test/*.class
The result is the alert below and the MyPublicClass.jar file containing .class files.
In result, I get the MyPublicClass.dex file containing exactly four Java classes compiled to DVM bytecode.
dex file limit
A popular error alert in Android goes as follows:
trouble writing output:
Too many field references: 131000; max is 65536.
You may try using –multi-dex option.
What does it mean? It means that your .dex file contains too much code and that you should check whether the imported libraries are redundant – if they are, remove them. Another solution is to separate the code into multiple .dex files, so called “multidex”. This operation may have certain side effects, such as:
Application Not Responding (ANR), i.e. temporary stopping of the application caused by reading in too big .dex files.
Applications based on multidex may be unable to work with systems lower than Android 4.0 (API level 14).
In this article, I wished to show you the differences between the structures of .class and .dex files in Java and Android. A programmer coding in one of these environments may not need to remember about these structures. However, there are cases when this knowledge proves useful, e.g. when you’re trying to start a .class file: if you receive the error alert java.lang.UnsupportedClassVersionError, you know it comes from the version sector describing the version of Java with which the file cooperates.
On the other hand, if you work in Android, you must remember about optimizing the code as such: you should limit the number of imported libraries to a minimum, and if there are any unexpected problems, such as exceeding the number of methods in the file, then, knowing that the .dex file contains all the compiled classes, including libraries, you can consider two solutions – removing some libraries or optimizing the code.
There are many other situations in which the awareness of the differences between those two types of files is important. They usually turn up while you’re working.