In this post, we are sharing a general insight into how java/kotlin code is converted to an apk file that is ready to be deployed on an android device. You might have already come across topics like JVM(Java Virtual Machine), Dalvik, ART, JIT(Just In Time compilation), AOT (Ahead Of Time Compilation), etc. Anyways, let’s try to break down some of these topics here.
Let’s just go through each of these topics one by one.
What is JVM?
JVM(or Java Virtual Machine), as you might already know, is just a runtime environment to execute java applications. These environments basically help to convert compiled java code to bytecode(.class file).
In Android, we can’t use the JVM directly since JVMs are designed for machines with high computational power, storage, etc like PCs. Android Devices, when compared with the PCs offer very less RAM and processing power. So, in order to make the JVMs compatible with android devices, the JVM runtime was modified accordingly to build an environment that is suitable to be run on android devices – it was named Dalvik. Dalvik continued to be the default runtime environment for the android version till 4.4 Kitkat. Dalvik followed the JIT(Just In Time) based compilation process. According to JIT, the conversion to machine code happens during app opening.
JIT-based compilation causes lots of performance issues like battery drain, app lag, etc. This forced the need for a newer, better-optimized JVM for android devices which resulted in the development of ART(Android Runtime).
Advantages of ART over Dalvik
ART(Android Runtime) follows an Ahead Of Time (AOT) based compilation process, which means that, here, the conversion to machine code happens during the installation process itself. Hence it substantially reduces the effort required during the app since machine code conversion is already over. ART also provides an improvement in Garbage Collection(GC) process. But AOT-based processes increase installation time and apk size. Even though the AOT-based environment increases the installation time of the app, it offers significant performance improvements, so the trade-off is acceptable.
Dalvik runtime was replaced by ART but the Dalvik bytecode(.dex) is still in use and ART is capable of running dex bytecodes.
The Android Dexer
The android dexer was responsible for converting java bytecode to dex bytecode, which was replaced by d8, starting from android studio 3.1. d8 produces smaller dex files and better performance when compared with dx.
Desugaring and the advent of D8 dexer
Before we go through the D8 dexer in detai,l we must know what caused its formation. You must have used the jack toolchain briefly which helped us in supporting java 8 language features on android, but it was deprecated because of its direct conversion of source code to dex bytecode.
As a result, android’s default toolchain was updated with java 8 language features like the super cool lambdas. Wait.., but the android runtimes (Dalvik or ART) won’t support java 8 features. Yes, you guessed it correctly – it won’t, but the updated android toolchain supports a feature just to overcome this big hurdle – Desugaring. Since the android runtime won’t support java 8 language features, desugaring can be used to convert java 8 bytecode to java 7 bytecode.
Hence we can now use java 8 features like lambda functions on android. As I said before, D8 offers significantly higher runtime performance when compared with dx. It also helps in generating smaller apk files. Now, we will see how to integrate desugaring with D8. As seen before, in the default toolchain, desugaring is a separate step that runs before the execution of android dexer. With D8, we can integrate desugaring along with it. The integrated desugaring will further improve the build time.
Finally, What is R8?
R8 basically provides all the features offered by D8, but with a lot of add-on features. So R8 can be called a successor to D8, but not exactly. Confused? Don’t worry, let’s see what features R8 offers.
Apart from supporting Java 8 language features like D8, R8 also enables further optimisation of the dex bytecode. As of now, proguard is run as a separate step in the build process, but R8 provides features like optimisation, obfuscation, removing unused classes, etc (which proguard already offers) within itself. Even though R8 only offers a subset of features provided by proguard, now we can perform the entire java/kotlin bytecode to dex bytecode conversion process within a single transformation itself (which further improves build time), which is – R8. Hence, you must always keep in mind that R8 is not a complete replacement for proguard.