Android’s D8 dexer and R8 shrinker
There is quite a bit of complexity to packaging an Android application. Xamarin.Android has to run through the same build pipeline as Android apps built in Android Studio–with the addition of supporting the .NET code we know and love.
For instance, a typical Xamarin.Android APK file contains binaries compiled from various languages:
- Java code: in an Android-specific DEX (Dalvik-executable) format
- C/C++ code: .so files such as the Mono runtime
- .NET code: .NET .dll files reside in an assemblies directory in the APK.
And this is just code! There is, of course, even more stuff in your APK such as layout files, image resources, assets, etc.
If we dive deeper into how Java code is compiled, the following steps produce the final DEX file for an Android app:
- javac compiles Java source code to .jar files.
- desugar may need to run, in order to enable Java 8 features on Android.
- ProGuard may run to strip out unused Java code and make optimizations.
- DX finally converts all compiled Java code to Android DEX format.
We can now start to see why “build performance” is a recurring concern for all Android developers.
D8: a next-generation DEX compiler
Google recognized the performance implications of having to run several different tools to generate DEX files. In 2017, Google announced D8 as a DEX compiler that was rewritten from the ground up to perform many of these steps at once. The implications being that D8 could compile to DEX in 30% less time and produce smaller DEX files than DX. D8 also implements “desugaring”, so it can accept Java 8 code as input by default.
As an extension to D8, R8 is a Java code shrinker built as a replacement for ProGuard. R8’s main benefit is that it can shrink code at the same time as producing DEX files–in the same step, same process. Both D8 and R8 are built to be backwards compatible with Android applications that are currently built with DX and ProGuard.
With D8 and R8, a final DEX file can be produced by just two command-line tools:
- javac to produce .jar files
- R8 to produce the final DEX file
What does this mean for Xamarin.Android?
Xamarin.Android, of course, has to compile Java source code into DEX format as part of the build. It is natural for us to take advantage of D8, since build performance and smaller APK sizes are key areas we are focusing on.
As of Visual Studio 2019 Preview 2, you can enable D8 by setting an MSBuild property in your application’s csproj file:
<PropertyGroup> <!--Other properties here--> <AndroidDexTool>d8</AndroidDexTool> </PropertyGroup>
If you are already using ProGuard, R8 can be enabled in a similar fashion:
<PropertyGroup> <!--Other properties here--> <AndroidLinkTool>r8</AndroidLinkTool> </PropertyGroup>
ProGuard is a bit of work to setup, but well worth the effort if you are trying to cut down the size of your APK. Ensure your application works properly when using ProGuard before trying out R8. Any proguard.cfg rules you are currently using should continue to work with R8, but it is possible some differences exist.
What are the results?
As promised, we saw some noticeable improvements when enabling D8 in a “Hello World” Xamarin.Forms Android application.
A rough estimate of the improvements we saw using D8:
- ~25% reduction in DEX compile times. Contributes to a 5-10% improvement in overall Xamarin.Android build times.
- ~15% reduction in DEX file size. Contributes to 2-3% reduction in overall APK file size (DEX file are compressed).
Long term, Google will likely sunset support for DX in the Android SDK as well, so eventually D8 will become the default DEX compiler. Try out D8 and R8 today, and let us know how things are working in your apps!
Want to learn more?
To learn more about Xamarin.Android’s D8 and R8 integration, see our documentation on Github.
For a deep dive on how R8 is being developed for Android, I would highly recommend checking out Jake Wharton’s talk from DroidCon 2018.