April 22nd, 2019
Mobile devices are limited in many ways. Whether it’s how much of a charge your battery can hold, the amount of storage you have left, or the speed of your internet connection to view more cat gifs. We need to keep this into consideration for our users to give them the absolute best mobile experience.
Bigger is not better
When it comes to application size, bigger is not better. In fact, it’s one of the key factors a user takes into mind when considering installing or uninstalling your application.
Imagine if a user ran out of space on their device and your app was one of the biggest offenders for used space. It would be quite a shame for your application to be one of the first to be uninstalled, purely for how large it is.
By shrinking our APK size, we can also improve how:
- Fast our app loads.
- Much memory our app uses.
- Much power our app consumes.
- And much more.
Analyzing the APK
The first step to understanding what we can shrink within our Android applications is analyzing the APK. There are a few great disk usage tools that will allow you to do this.
Integrated inside Android Studio or usable from the command line, you can use apkanalyzer as a way to provide immediate insight into the composition of your APK. You can even compare differences between two APKs. Using APK Analyzer will help you reduce the size of your APK by inspecting your DEX files and Android Resources.
Viewing the APK from the top-down
For our example, we will be using the Smart Hotel 360 Mobile Application on GitHub. Feel free to grab a copy of this application to follow along. Let’s take a look now.
Looking at our APK from the top-down, we can see that the raw APK size is 34.6 MB and the download size is 23.4 MB.
There are four main areas that contribute to our large APK size:
- res – This folder includes all the files under your Resources folder.
- assemblies – This folder includes all of the assemblies required for your application.
- lib – This folder includes native libraries for the respective ABIs that your application supports.
- classes.dex – This file holds all of your application’s byte code that is executed by DVM or ART.
Distribution of APK Size:
- res – 11.5MB
- assemblies – 14.8MB
- lib – 3.9MB
- classes.dex – 2MB
- Total – 32.2MB, accounting for 93% of total APK size (34.6MB)
Inspecting the res folder
Typically the most impactful area contributing to our APK size coming in at 11.5MB, the resources in our project tend to be an afterthought of creating our application. We can optimize our resources by removing unused resources, resizing individual resources to mobile sizes, converting drawables to webp/vectors, and much more.
Note: If you’re using Xamarin.Forms, you’re most likely setting your images to
EmbeddedResource. Since this embeds the resource into the assembly uncompressed, this can add quite a bit to your assemblies size. It might be a better decision to set these items as Android resources as you can optimize these further.
Removing Unused Resources
When developing a project and getting to the point of release, it’s very likely we’ll have left over resources that aren’t used within the application. Remove any unused resources as a first go-through.
Google’s R8 tooling will also remove unused resources it finds within your application at build time.
Resizing and Optimizing Resources
Chances are, you have larger resources than you can actually display on a mobile phone in your project. These resources should be resized to a size closer to the largest device you plan to support while keeping in mind Android Screen Densities.
Alongside this, it’s very common that images are not using the proper file type for compression. For example, if you have a background or image that doesn’t require transparency, it’s typically a better idea to use JPG or WebP.
Convert images to WebP
Similar to the optimization tip above, we can convert our images to WebP, a file format similar to JPG that provides lossy compression alongside the transparency PNG offers. It’s the best of both worlds. Let’s now convert all of our resources to webp.
Before: res – 11.5MB
After: res – 2.1MB
Total Saved: 9.4MB
Use VectorDrawable when you can
Instead of creating multiple density-specific versions of an image, you can create one vector graphic. Vector graphics create an image using the XML which defines paths and colors. Learn more about Vector Graphics.
Inspecting the assemblies folder
Second, the assemblies folder contributes towards a nice chunk of our APK size with 14.8MB. The next step for us is fine-tuning the Mono Linker to shrink each assemblies’ size.
Running the Mono Linker
The Mono Linker will remove any unused types and members within your assemblies based on a static analysis of your application.
By using the Mono Linker on it’s most aggressive linking setting: Sdk and User Assemblies, we can apply linking to not only Sdk Assemblies, but also on User Assemblies.
Keep in mind that using this setting is very prone to running into Linker errors. You can learn more about controlling the linker here.
Before: assemblies – 14.8MB
After: assemblies – 12.6MB
Total Saved: 2.2MB
Inspecting the lib folder
The lib folder comes in at 3.9MB. We can’t do too much around the lib folder other than be mindful that for every additional ABI we support in our .APK, the additional multiplier of APK size. For example, if we added a 64-bit compatible ABI, we would double our size that the lib folder contributes to our application. To avoid this, we can create ABI-Specific APKs or use Android AppBundle in the future.
Android App Bundles Coming Soon
If you haven’t heard about Android App Bundles, Xamarin.Android will soon support App Bundles to provide users with optimized APKs per their device specifications when downloading an app from the Google Play Store or various Android App Stores. This will allow you to not have to worry about certain types of resource qualifiers like locale, screen orientation, and pixel density being included in the APK resulting in a typical “fat package”.
Alongside that, it would only include ABIs the device supports. The final APK delivered to the user’s device would contain the minimal APK required for the application to run on device.
Inspecting the DEX file
Other than being 2.0MB towards our APK size, the classes.dex file does not have much insight other than seeing a distribution of where methods are defined inside your application and third-party libraries. There will be many methods that will be stripped in a result of applying ProGuard or R8.
Enough inspecting, let’s start applying some techniques to improve our APK size.
Using ProGuard / R8
Enabling ProGuard or R8 will remove any unused code from your APK.
Before using ProGuard, we can see our classes.dex file at 2MB.
Let’s enable ProGuard within our application:
As you can see, we’ve removed 0.8 MB from ProGuard alone!
In the future ProGuard will be replaced with Google’s R8 tooling by default which is backwards-compatible with existing ProGuard rules. For information on using R8 instead of ProGuard, see our article on d8 dexer and r8 shrinker.
Before: classes.dex – 2.0MB
After: classes.dex – 1.2MB
Total Saved: 0.8MB
Through various techniques of shrinking our Android APK, we were able to take our package from 34.6MB to 21.9MB in just a few minutes. That’s a 36% decrease in APK size!
Now it’s your turn to shrink your Android APK!
Program Manager, Xamarin.Android