Engineering Speaks: Putting SignEasy for Android on a diet

Over the past few months, the SignEasy app for Android has gone through multiple iterations of improvements. We’ve taken big strides in trying to improve the experience for our users, and after almost a year of effort, we’ve had the app go from looking like this in June 2015 to this in June 2016.

SignEasy for Android change

The revamped app is built around the principles of Material Design (an interesting story on its own), feels faster and has an improved signing experience. But like all good things this too came at a cost, with the app bulking up and touching almost 35 MB. Unacceptable! We could feel the pain of our users and decided it’s time to put the app on a much-needed diet. Here are some techniques we used.

The many, many libraries

Apps tend to use a host of libraries for their functioning, and I don’t just mean that 3rd-party library you added for animations. You need to additionally include SDKs for backward-compatibility, for any of the Google APIs, or even to make your app material. Every dependency you add may contribute to the APK size significantly, especially if your app has been in production for a long time. A good starting point is to trim off your app of any unnecessary libraries.

Unnecessary baggage

This is a great tool to see which libraries your app is using with a count of the number of methods it has. The larger the number, the more it ultimately contributes to your APK. Remove all the unused libraries, especially the bulky ones.

ProTip: If your app is using Google Play Services, make sure you aren’t including the entire package into your build. From v6.5, you can selectively include only those libraries that you need. It’s a neat little trick that helped us cut off MBs from our app.

Always ProGuard

Ensure you’re applying ProGuard to your APK. It’s a very effective tool that removes all unused classes, methods, fields from the packaged app including any from the libraries.

If you’re using AppCompat-v7 or support library-v4 in your app (which you probably are), make sure your ProGuard file doesn’t have any of these lines.

-keep class** { *; }
-keep class** { *; }

The support libraries are large on their own, and it helps if you allow ProGuard to remove all unused classes from them.

ProTip: If you’re using AppCompat’s SearchView in your app, adding the above two lines may cause things to break. This is because any of the AppCompat elements that are used in XML as a string will not be recognized by AAPT and the class is stripped off by ProGuard. To avoid this, add this line back to your ProGuard configuration.

-keep class { *; }

(This works with any class that is used in XML as a string.)

Minifying and shrinking

A simple technique is to tell Gradle to shrink resources and remove unused resources for you at the time of packaging your app. In your build.gradle file add the following to the build type you want to affect:

buildTypes {
        release {
            minifyEnabled true
            shrinkResources true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), ''

Managing drawables

With the diverse array of Android devices, developers usually add assets for 5 different densities (mdpi, hdpi, xhdpi, xxhdpi, xxxhdpi), not to mention possibly different assets if you have a tablet specific UI, like we do. All these images packaged into a single APK contribute to the bulk significantly.

Be ruthless

I once got a great piece of advice from a Google Developer Advocate: Identify the devices most of your customers are using, and if they don’t fall in the mdpi-hdpi buckets, remove assets for these two densities. Sounds a little risky at first, but it’s one of the most effective ways of getting rid of a good chunk of those MBs. Sure, it takes slightly longer for the xhdpi asset to be rendered on these devices, but it’s hardly noticeable.

Furthermore, as mentioned earlier, the SignEasy app has some assets specific to the tablet form-factor. Due to their larger dimensions as compared to phone assets, these images tend to be bigger in size as well. We tried to figure out which density bucket most of our tablet users fall into (this helped a lot) and were surprised to see most tablet devices are of either mdpi or xhdpi density. Removing all other assets for tablets affected the APK size drastically.

Adopt vector drawables…seriously

As an Android developer, one of the best practices you can introduce into your team’s app development process is forcing your developers and designers to start using SVGs for image assets to be included in your project as vector drawables. This allows you to replace multiple PNGs with a single vector graphic that maintains its sharpness regardless of the device density it is rendered on, aka pure magic. Although first available only to Lollipop and above, support library 23.2 includes support for vector drawables all the way back to API 7. Plus, Android Studio 1.4 introduced a nifty little tool that helps you in importing vector graphics into your project. This will definitely help in keeping the APK size down.

Everything unused

Here are some techniques that will help you chip away on those smaller unused resources. Although these don’t make a big impact on the APK size, every KB ultimately counts. Plus, it helps keep your code base clean.

Android Lint

This is a very handy tool built right into Android Studio (but oddly hard to find). Go to Analyze > Run inspection by name. In the Inspection dialog, type in unused resources, select your scope (whole project recommended) and run. Lint analyzes all the resources (strings, drawables, dimens, etc.) that aren’t being used anywhere in your code. You can choose to go to each suggestion and delete the resource or simply ask Lint to delete everything for you. It’s easier to do the latter and then add something accidentally deleted back, especially if you’ve never done this before, as Lint will probably end up suggesting a very large number of unused resources.

Pick your languages

An interesting technique that was discussed in Google I/O 2016 was specifying which languages you localize in in your Gradle script. This strips away all other string files that could’ve been added by other libraries in languages you don’t even support. To do this, specify the languages you support in your app level build.gradle file:

defaultConfig {
resConfigs “en”, “es”, “ja”, “in”

Splitting your APK

The Gradle build system allows you to produce multiple APKs for a build depending on the split criterion. Although this isn’t encouraged by the Android documentation, sometimes the most effective way of trimming the APK is by, er, chopping it up. You can do this in various ways:

ABI split

This is especially useful if your project includes libraries written in native code (.so files) which support different CPU architectures. You can split your APK per architecture so that a user running an arm ABI doesn’t receive the code for x86, and so on. To do this, add the following to your app level build.gradle file:

android {
splits {
    abi {
        enable true
        include 'x86', 'armeabi-v7a', 'mips'
        universalApk false

Density level split

Using this you can produce multiple APKs split by the density of the device so that a user with a xhdpi device doesn’t carry assets meant for xxxhdpi. To do this, add the following to your app level build.gradle file:

android {
  splits {
   abi {
enable true
include 'mdpi', 'hdpi', 'xhdpi', 'xxhdpi', ‘xxxhdpi’
compatibleScreens 'small', 'normal', 'large', 'xlarge’

For a comprehensive list of more split options, read this.

I also recommend watching this Google I/O 2016 talk and also reading this related blog for more ways to reduce the app size.

After painstakingly applying most of the above techniques, the SignEasy Android team has successfully decreased the app size by almost 30%. We aren’t completely satisfied yet and will keep trying out ways to go easy on our users’ device storage. Remember, every KB counts.

For questions, thoughts, and comments, find me on Twitter @ApoorvaTyagi.

Recommended Reads

Made with in USA & India