Shrink apps with Gradle for Android

Versão em português aqui.

It is almost impossible to write a good Android App without using many libraries. Several features like networking, image loading, caching, database operations, social networking and many others can be very easily incorporated with libraries from different sources. That makes development much easier and faster, but also bring some consequences.

The problem

Every library added to a project brings many possibilities and features. To achieve that, the libraries usually contain a lot of code files and resources. Almost every library I use contain more features than what I need. That creates a sort of waste: my applications have a lot of stuff that are never actually used. More than that, the size of the packages is growing in a fast pace. One of the apps I have been working reached 6.5MB recently. That’s a lot, I can do better.

That’s a big apk.

Enters Gradle

The Android plugin for Gradle has some nice features to improve the applications. One of those is the option to minify the source code. With this option enabled, the source code will be decreased by the reduction of the identifiers and deletion of unused parts. The source will also be obfuscated, making harder do read if it gets decompiled. Let’s enable it on the build.gradle.

//...
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
}
//...

The result is a 6.0MB apk. That’s a good start.

A bit better.

Resource Shrink

Another cool option from Android Gradle is the resource shrink. When this option is enabled, unused resources are removed from the final apk, reducing it’s size. Let’s enable it:

release {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
}

The result is, well, disappointing.

No changes in file size.

Reading a little further in the resource shrink technical doc, there are some options to make the shrink more aggressive. In the case of the application I am working, the option resConfigs did a very good job. By creating a list of the resources configuration used by the application, the tool can eliminate more things.

This post previously stated that it was possible to apply a resource configuration for removing screen resolutions. This option was removed in more recent versions from Android Build tools and replaced by the option to generate multiple APKs.
resConfigs "en", "pt"
Better now.

By decreasing source code size and removing unused resources, the apk was reduced from 6.5MB to about 5MB. That’s a good improvement.

But not that fast

One of the reasons that make some people disable the minify option and, by consequence, the shrink option is that it can create runtime issues. In the case of my application, gson wasn’t enable to convert the JSON data returned by the server. To make that work, I had to add a big set of the instructions to my proguard-rules.pro file. The initial set can be found at the gson repository. To complete the set, I had to make the tools ignore (or keep) the classes used with gson:

-keep class com.package.model.** { *; }

The libraries also have it’s own configurations. Glide has the instructions detailed in their page. Okio, that is used in a indirect way, works ignoring the warnings:

-dontwarn okio.**

SnappyDB is more tricky:

-dontwarn sun.reflect.**
-dontwarn java.beans.**
-keep,allowshrinking class com.esotericsoftware.** {
<fields>;
<methods>;
}
-keep,allowshrinking class java.beans.** { *; }
-keep,allowshrinking class sun.reflect.** { *; }
-keep,allowshrinking class com.esotericsoftware.kryo.** { *; }
-keep,allowshrinking class com.esotericsoftware.kryo.io.** { *; }
-keep,allowshrinking class sun.nio.ch.** { *; }
-dontwarn sun.nio.ch.**
-dontwarn sun.misc.**

-keep,allowshrinking class com.snappydb.** { *; }
-dontwarn com.snappydb.**

In the end, there is no choice besides searching for the specific configurations to each library. The point here is to pay attention to each of those configurations. One of the suggestions to make SnappyDB work recommended to disable the resource shrink. That’s is something that won’t happen :) Every time you change one of those options, check the final size again and look for potential runtime errors.

Going further

The apk size probably can be more compressed. At some moment I will go deeper, following the instructions presented by Cyril Mottier in his blog. That will certainly takes more time than adjusting some build files as I did. The result from the adjusts in build files, in the other hand, was very good. Removing 1.5MB of unused stuff from your final APK is certainly a good thing. From now on, minify and shrink configurations are my default choices and will certainly be very savy.