Desugaring in Android
Since Kotlin became the recommended language for Android development, most of the developers have moved from Java to Kotlin. Kotlin has undoubtedly exceeded all the expectations and is now the favorite language for many. Speaking for myself and from an Android developer’s view-point, if someone asks me to switch back to Java, I’ll think a million times before giving up all the perks provided by Kotlin-first APIs.
However, people at Oracle are constantly making efforts to improve Java. For instance, Java 10’s var feature to declare variables and Java 8’s time API & stream API . Time API actually has a ton of improvements than the classic DateTime API such as Duration , Period , and more. I’ll leave that part for you to explore and now let’s talk about what desugaring is & what it has to do with all this stuff.
What is Desugaring?
When any new Android OS is developed, it is shipped with a bunch of Java classes that provide support to the apps requiring those classes to function. Let’s consider the Time API in this case. It was added to the Android API level 26 and hence, the apps using this library will crash on lower API levels such as Marshmallow (API 23).
Here’s when desugaring comes into the picture. It allows lower API levels to work with new Java libraries. We’ll see how it does the trick but before that, let’s fire up Android Studio for a quick demo to make things more clear.
Practical
Keeping it short and simple, I’ll just print out the current month using the new Time API and I’ll test it on Android R (API 30) and Android M (API 23).
tv_month.text = LocalDate.now().month.name
As expected, it works on API 30 but fails with a NoClassDefFoundError exception on API 23. To fix this, we need to enable desugaring in our project using the following steps.
- Set Android gradle plugin to 4.0 or higher.
- In compile options, enable coreLibraryDesugaringEnabled flag and set Java target & source compatibilities to Java 8.
- Enable multidex (for projects supporting API level 20 or below)
- Add the desugaring dependency
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.0.5'
And voila! The app can now run on low-level APIs as well. Here’s the demo link for you to play around with.
Under the hood
So what actually happened here? How did the missing Java classes in the OS suddenly show up? It’s all possible because of D8/R8 tools. Before jumping into how they do this trick, let’s get into a bit of history to understand what these tools exactly are.
Exploring D8/R8
Android developers have been using ProGuard for a long time now and it has proved to be a “go-to” tool when it comes to code shrinking and obfuscation. Let’s see how code flows when using ProGuard.
The source code is converted into bytecode by JVM. This bytecode is optimized by ProGuard and finally, it is converted into a dex executable by the dex compiler.
As the code has to pass through a couple of phases before it is bundled into APK, the build time increases. To reduce this time, Google introduced Jack & Jill compiler around 2015 which did the same thing but in a single phase.
Due to constant advances in technology and tools, this solution wasn’t able to satisfy the emerging requirements. Hence, Google abandoned it around 2017 and they went back to the classic way of compilation. But this time, they rewrote the dex compiler and introduced a tool called D8 .
This proved to be very successful and was even able to support Kotlin’s growth. Google further introduced R8 code shrinker which basically is a combination of ProGuard and D8. This helped reduce a phase in the compilation process.
Uggh..! That’s a lot of theory, isn’t it?
Coming back to desugaring, the D8/R8 phase is the real magician that provides the missing Java classes. While converting our app’s source code into dex code, it also adds the dex code for new java libraries which are then finally bundled in the APK. This process is represented in the following diagram.
And that’s how older android API levels can use the new Java libraries!
Conclusion
Desugaring, in simple terms, is a tool to provide backward compatibility for new Java libraries. When we think of it, it’s a very simple concept but to understand it inside-out, we need to know some stuff that revolves around it. I hope you grabbed something useful out of this blog. See you in the next one, till then. Stay Safe!