Sitemap
Dipien

Productivity tools & ideas for Android, Kotlin & Gradle developers.

10 ideas to reduce your APK size [Part II]

Less is More: A Comprehensive Guide to Reducing APK Size and Optimizing Your Android App’s User Experience

6 min readMar 10, 2023

--

Press enter or click to view image in full size

In this series of 3 articles, we will explore some ideas to minimize your app’s download and install size. This is the second part of the 10 ideas to reduce your APK size series. You can also read Part I & Part III.

1. Remove unnecessary generated code

It is important to be aware of the impact of any automatically generated code on your app’s size. Some protocol buffer tools, for instance, may create an excessive number of classes and methods, which can significantly increase the size of your app by two or three times. Therefore, it is recommended that you carefully evaluate the footprint of any code that is automatically generated.

2. Remove non-production code & resources

The Android Gradle Plugin logically groups source code and resources for each module into source sets. A module’s src/main/ source set includes the code and resources used by all its build variants. Additional source set directories are optional. Using source sets helps organize files and resources that Gradle should only use when building certain versions of your app.

So, in conclusion:

  • src/main/This source set includes code and resources common to all build variants.
  • src/${buildType}/This source set includes code and resources only for a specific build type.

For example, to generate the debug build of your app, the build system merges code and resources from the following source sets:

  • src/debug/ -> the debug source set (if present)
  • src/main/ -> the main source set

If you have non-production code (for example, debug code) under src/main, your release APK will have it. Organizing your code under the correct source set will help you to reduce your APK size. You can read more about this topic in the following article:

3. Remove your unused resources

Google has a static code analyzer called the lint tool, which can detect resources in your app’s res/ directory that your code doesn’t use. If the lint tool identifies a resource that may be unused in your project, it prints a message similar to the following example.

res/layout/preferences.xml: Warning: The resource R.layout.preferences appears
to be unused [UnusedResources]

The following plugin can help you automatically remove unused android resources from your project. It not only detects them, but it also removes them:

You can configure your CI tool to regularly run that plugin and send a pull request with all the removed resources. This will help with app size and also with build times because if you have unused resources in your project, each developer’s computer is wasting time processing them.

When you add libraries to your code, they may come with unused resources that bloat your app’s size. To address this issue, you can enable the shrinkResources option in your app’s build.gradle file. This will allow Gradle to automatically remove unnecessary resources from your code.

android {
buildTypes {
getByName("release") {
minifyEnabled = true
shrinkResources = true
proguardFiles(getDefaultProguardFile('proguard-android.txt'), "proguard-rules.pro")
}
}
}

To use shrinkResources, you must also enable code shrinking. During the build process, R8 first removes unused code. Then, the Android Gradle plugin removes unused resources.

For more info, you can read here.

4. Remove unused alternative resources

The Gradle resource shrinker is designed to remove only those resources that your app code doesn’t reference. However, it doesn’t remove alternative resources used by the different device configurations. To remove such resources, you can use the resConfigs property of the Android Gradle plugin.

For instance, if you are using a library that includes language resources, such as AppCompat or Google Play Services, your app may contain translated language strings for messages in those libraries, even if the rest of your app is not translated into the same languages. To include only those languages that your app officially supports, you can specify them using the resConfig property. Any resources for other languages are removed.

Here’s an example of how to limit your language resources to English and French:

android {
defaultConfig {
resourceConfigurations.addAll(listOf("en", "fr"))
}
}

5. Reuse resources

While it’s possible to include separate resources for variations of an image, such as tinted, shaded, or rotated versions of the same image, is recommended to reuse the same set of resources and customize them as required during runtime.

Android offers several utilities to modify the color of an asset, such as using the android:tint and tintMode attributes on Android 5.0 (API level 21) and higher. For lower platform versions, the ColorFilter class can be utilized.

In addition, rotated equivalents of another resource can be omitted. The following code snippet provides an example of flipping a “thumb up” image to a “thumb down” image by rotating it 180 degrees around the center of the image:

<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/ic_thumb_up"
android:pivotX="50%"
android:pivotY="50%"
android:fromDegrees="180" />

6. Support only specific densities

Android has a vast range of devices with varying screen densities, which are supported by the framework in Android 4.4 (API level 19) and higher. These densities include ldpi, mdpi, tvdpi, hdpi, xhdpi, xxhdpi, and xxxhdpi. However, it’s not necessary to export rasterized assets for each density.

If you’re aware that only a small portion of your users have devices with specific densities, it’s worth considering whether you need to include those densities in your app bundle. If you exclude resources for a particular screen density, Android automatically scales existing resources designed for other screen densities.

For apps requiring only scaled images, space can be saved by having a single variant of an image in drawable-nodpi/. It’s advisable to include at least an xxhdpi image variant in every app.

To learn more about screen densities, refer to the Screen Sizes and Densities documentation.

7. Use drawable objects

Certain images don’t require a static image resource, as the framework can draw the image dynamically at runtime. Drawable objects, such as those defined by <shape> in XML, occupy very little space in the APK. Moreover, XML Drawable objects generate monochromatic images that adhere to material design guidelines.

8. Render images from code

Another way to reduce the size of your APK is to render your images procedurally. Procedural rendering eliminates the need to store an image file in your APK, thus freeing up space.

9. Migrate your images resources to WEBP

If you’re targeting Android 3.2 (API level 13) or higher, you can consider using the WebP file format instead of PNG or JPEG files for your images. The WebP format supports both lossy compression (like JPEG) and transparency (like PNG), but can provide better compression than either format.

You can convert your existing BMP, JPG, PNG, or static GIF images to the WebP format using Android Studio. For more information, please see Create WebP Images Using Android Studio.

10. Crunch PNG files

During the build process, the aapt tool can optimize the image resources located in res/drawable/ using lossless compression. For instance, if a true-color PNG doesn’t require more than 256 colors, the aapt tool can convert it to an 8-bit PNG with a color palette, producing an image of equal quality with a smaller memory footprint.

However, note that the aapt tool has the following limitations:

  1. PNG files in the asset/ folder are not shrunk by the aapt tool.
  2. The aapt tool can only optimize image files that use 256 or fewer colors.
  3. The aapt tool may inflate PNG files that have already been compressed. To prevent this, the isCrunchPngs flag can be used to disable the process for PNG files:
buildTypes.all { isCrunchPngs = false }

10 more ideas on Part III

You can follow our DipienMedium Publication for more productivity tools & ideas for Android, Kotlin & Gradle developers.

Support Us

There are different ways to support our work:

  • With Bitcoin Lightning using Alby:
  • With PayPal or a credit card using Ko-fi.

--

--

Dipien
Dipien

Published in Dipien

Productivity tools & ideas for Android, Kotlin & Gradle developers.

Maxi Rosson
Maxi Rosson

Written by Maxi Rosson

I write about Productivity tools & ideas for Android, Kotlin & Gradle developers. Founder of smarthomecompared.com where I compare the best Smart Home device.

No responses yet