Build your Android app Faster and Smaller than ever

Last month I got a great opportunity to give a talk in LINE DEVELOPER DAY 2018. It’s a special moment for me because it’s my first talk in Japan. Before being speakers this event LINE staffs have to submit their talk to the global team.

The topic I submitted are tips how to build Android app faster and how to generate smaller APK by Android Studio and Gradle that I’ve gathered from my experiences, Google I/O and especially from Developer Build Clinic. So Developer Build Clinic is one on one consulting in build performance improvement provided by Android Studio team.

Developer Build Clinic by Android Studio team

In this blog I want to share with you these tips and the app I bring to show you in this experiment is LINE MAN Driver app.

https://lineman.line.me/

For people who don’t know what LINE MAN is, it’s an on-demand assistant app with professional services including Food Delivery, Convenience Goods Delivery, Messenger Service, Parcel Service and Taxi to serve all of Thai user needs at any time.

Please noted that actual results may vary because it depends on your project characteristics and your build environment e.g. project size, resources, dependencies and machine performance.


Tips for building app Smaller

The size of your APK has an impact on how fast your app loads, how much memory it uses, and how much power it consumes. I think most of you know APK size is an important factor in your user engagement. Let’s see current app size in LINE MAN Driver

Current app size is 9MB

Quick recommendation to optimize your APK is to use APK Analyzer in Android Studio. You can open Android Studio and select “Profile or debug APK” then browse your APK file. This tool will breakdown your packages in your APK file e.g. layouts, images, classes, libraries and etc. So you’ll know which part is big or which part is small then you can prioritize which one you should optimize.

APK Analyzer

Tip 1: Remove unused resources

Most of you are developing in legacy projects and there are a lot of images, layouts, and strings that you never used but you don’t know and you don’t want to remove it yourself because you are afraid you will make your app crash right? So In Android Studio, it provides you a menu called Remove Unused Resources.

It is very helpful for us because it’ll find unused resources automatically then you can remove them in one click.

Tip 2: Add just Dependencies Needed

Some dependencies they include a bunch of library inside like play-services and Facebook SDK. If you don’t specify libraries you need you will get entire of them that make your app fat. For example, if you want to use Google Auth you should specify “com.google.android.gms:play-services-auth:16.x.x” instead of “com.google.android.gms:play-services:16.x.x

You can break down dependencies in your project by a command line below

$ ./gradlew app:dependencies

You will see all of dependencies you use in your project then make sure you only use dependencies you need.

Tip 3: Build Multiple APKs for Screen Densities

By default Android Studio will generate a universal APK that include all of screen densities inside. In this tip you can specifically exclude or include screen densities you want to support in app/build.gradle and Android Studio will generate multiple APKs for you.

android {
splits {
density {
enable true
      // Specify a list of screen densities which Gradle won't create multiple APKs for
exclude 'ldpi', 'mdpi'
      // Specify a list of compatible screen size for the manifest
compatibleScreens 'small', 'normal', 'large', 'xlarge'
}
}
}

So you need to upload all of them to Google Play and finally your users will download only APK which match with their screen densities.

Tip 4: Build Multiple APKs for ABIs

This tip is similar to the previous tip but this tip is for supporting Application Binary Interfaces(ABIs). Now a day I think there are 7 CPU architectures in Android market and 3 of them are hard to find(mips, mips64, armeabi) so you can specify ABIs you want to support in app/build.gradle and Android Studio will generate multiple APKs for you.

android {
splits {
abi {
enable true
      reset()
      // Specify a list of ABIs that Gradle should create APKs for
include 'x86', 'x86_64', 'arm64-v8a', 'armeabi-v7a'
      // If you don’t want to generate a universal APK that includes all ABIs.
universalApk false
}
}
}

After that you need to upload all of them to Google Play and finally your users will download only APK which match with their CPU architectures.

Tip 5: Build an APK with Specific ABIs

This tip is different from multiple APKs. You can specify CPU architecture you want to support and Android Studio will generate only one APK.

android {
defaultConfig {
...
ndk {
abiFilters 'x86', 'x86_64', 'arm64-v8a', 'armeabi-v7a'
// armeabi, mips and mips64 has removed since NDK r17
}
}
}

In my experience I prefer this tip than build multiple APKs because I used to find some crashes in some devices from multiple APKs.

Tip 6: Remove Unused Alternative Resources

Sometimes you create a local app and you only want to target some specific languages. But there are some dependencies that include many languages around the world and you don’t need all of them. So you can use “resConfigs” property to specify languages you want to support and your app size will be smaller.

android {
defaultConfig {
resConfigs 'en', 'th'
...
}
}

Tip 7: Shrink Unused Code and Resources

By default minifyEnabled in Android Studio is false but I think many of you set it to “true” to minify and obfuscate your code for smaller and security reasons. I recommended you to add “shrinkResources” in app/build.gradle to remove unused resources after minify process. Because in minifying, gradle will remove unused code and that code might refer to some resources.

Lastly I think most of you may not know “-optimize” So you can add “-optimize” after “proguard-android” it will do more optimization for your build and your app would be smaller in significant number.

android {
buildTypes {
release {
minifyEnabled true

shrinkResources true
      proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}

Please be reminded this process will take more time so you should use “-optimize” in release block only.

Tip 8: Use Shape Drawable

Sometimes I see developers use gradient background or rounded corner image as a Bitmap image. Actually bitmap image is bigger than Shape Drawable that provided by Android Studio because in Shape Drawable you can draw rectangle oval, rounded corner and other in XML format.

Tip 9: Use WebP

WebP is smaller up to 30% with the same quality. The only thing you need to be careful is the OS requirements. If you are using opaque background, WebPs API level 15 and above will support them. But if you want to support transparency background in your WebP then you need to support API level 18 or above and Android Studio also provide you an easy way to convert your image to WebP format. You can do a right click on an image you want and select “Convert to WebP” then you will see the screen like this example.

So let’s see an example result the image size was reduced to 15% of the original size with 90% quality.

Tip 10: Use VectorDrawable

Since API level 21 you can use vector instead of bitmap image and for sure vector is smaller than bitmap image because VectorDrawable can reduce its size to different screen densities with the same quality of image.

If you are supporting minSdkVersion 20 and below. Don’t worry you can use it. Because the Android team provides you a support library. So you just use support library version 23.2 and above.

Cumulative Improvement

So here is a cumulative improvement after I apply these tips

One more thing

Starting from Android Studio 3.2 onwards Android App Bundle is the new app publishing format that makes your app smaller in the easier way. You don’t need to add any one line of code you just export it with a new way. So when you download your app, Google Play Dynamic Delivery only delivers code and resources a specific device needs.

In this way you don’t need to do Multiple APKs yourself anymore.

Please be noticed, file extension is gonna be .aab and you need to take more time to build but it’s worth.


Techniques for faster app builds

Build speed is critical to your productivity that you as Android developers really want to improve. From this issue I have 10 techniques for building LINE MAN Driver app faster.

Before optimization I would like to show you current full building speed in LINE MAN Driver app by Android Studio 3.2.1

So building time in LINE MAN Driver now is around 3 minutes.

Technique 1: Use latest Android plugin

The first technique is to make sure you use the latest Android Gradle plugin. Because there are a lot of bug fixes and performance issue fixes.

buildscript {
repositories {
jcenter()
google()
}
  dependencies {
- classpath 'com.android.tools.build:gradle:3.0.0'
+ classpath 'com.android.tools.build:gradle:3.2.1'
}
...
}

Technique 2: Avoid Legacy Multidex

The Second technique is to avoid Legacy Multidex. As you know if your app exceeds 64k methods limit then you need to use Multidex. So if your minSdkVersion is 21 and below and you also use Multidex then you will use what’s called Legacy Multidex that make you slower in building.

To avoid Legacy Multidex you can define a new product flavor and specify minSdkVersion 21 or above in app/build.gradle.

productFlavors {
development {
minSdkVersion 21
...
}
}

Technique 3: Disable Multi APK

The third technique, you should disable multi APK generation for your development build because it will take time for packaging and creating these APKs. So you can disable it by adding these 2 lines of code in debug block in app/build.gradle

buildTypes {
...
debug {
splits.abi.enable = false
splits.density.enable = false

}
}

Technique 4: Include Minimal Resources

To minimize the set of resources that you package in your development build. By default the build system will include all languages and screen densities that your app and libraries use. So during development you don’t need to use all of them, you can use one set of those resources by adding “resConfigs” and specify the language and the screen density you need for development builds.

productFlavors {
dev {
resConfigs('en', 'xhdpi')
...
}
}

Technique 5: Disable PNG Crunching

By default AAPT will crunch your PNGs to reduce their size and that’s a great thing for your release APK but it’s not important for your development builds. To avoid PNG crunching you can use a property below and set it to false.

buildTypes {
...
debug {
aaptOptions.cruncherEnabled = false
...
}
}

Technique 6: Use Instant Run

By default when you click the RUN button the system will try to do a cold swap and the app needs to restart but when you click Apply Changes button the system will try to do a hot or warm swap first and that will push changes directly into the live process.

Technique 7: Disable Updating Build ID

Next technique is to disable updating Build ID in Firebase Crashlytyics. I think most of you use Crashlytics and by default Crashlytics will generate a unique build ID on every build. And You may not know they provide a flag for you to turn it off as below.

buildTypes {
debug {
ext.alwaysUpdateBuildId = false
...
}
}

So you need to be careful to set this flag to false in debug block only.

Technique 8: Don’t Use Dynamic Versions

Gradle provides a very convenient way to get the latest version of each dependency by adding a PLUS symbol at the end of dependency line. It will make gradle check for new version of that library every 24 hrs and building time will increase.

android {
dependencies {
implementation 'com.android.support:appcompat-v7:+'
...
}
}

Technique 9: Config gradle.properties

The code here are configurations that I collected from my Android development experiences. For me they are like a magic that help you build your app faster.

For instance, by default Android Studio will give you 1.5GB of memory that might be good or bad because it really depends on the characteristics of your project.

org.gradle.jvmargs=-Xmx4096m -XX:MaxPermSize=1024m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
org.gradle.daemon=true
org.gradle.parallel=true
org.gradle.configureondemand=true
org.gradle.caching=true
android.enableBuildScriptClasspathCheck=false

So I encourage you to try these configurations and I guarantee you will save a lot of building time.

Technique 10: Use R8 new code shrinker

What’s next? In the upcoming Android Studio 3.3 can use R8 the next generation of code shrinker. It will reduce unused code and resources and also minify your source code. So Android Studio claim building time and APK size will be smaller.

Cumulative Improvement

Let’s see new full building speed after applying from these tips. Now I spend around 1 minute in full building. So here are cumulative improvements, the full build is now 3X faster.


Summary

Let you imagine if your legacy projects are bigger than LINE MAN Driver app how much size you can reduce and how much time you can save.

Here are my slides and video from my talk in LINE DEVELOPER DAY 2018

So I hope these tips and techniques will help you increase more productivity. Enjoy coding, Thank you!