Sitemap
Dipien

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

30 ideas to reduce your APK size

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

16 min readMay 22, 2023

--

Press enter or click to view image in full size

When it comes to Android apps, the download size and install size are two different things.

The download size is the amount of data you need to download from the internet to install the app on your device. This includes the app’s installation file (APK) with all the files required to run the app on that particular device.

On the other hand, the install size is the amount of space the app takes up on your device once it’s been installed. This includes the APK file, as well as any additional files or data that the app generates or downloads during use.

It’s worth noting that the download size is usually smaller than the install size, as some files may be compressed during the download process to reduce the amount of data you need to download. Additionally, some apps may download additional files or data after installation, which can increase the app’s overall size.

Knowing the difference between download size and install size can be useful when managing the storage space on your device. If you’re running low on storage space, you may want to consider deleting apps with large install sizes, even if their download sizes were small.

Both are important to reduce. A download size reduction can reduce download times, which is particularly helpful when your internet connection is not good. For example, in emerging markets where devices connect to spotty 2G and 3G networks or have pay-by-the-byte plans, users tend to avoid downloading apps that appear too large. An install size reduction can help users with devices without too much storage available.

This post will explore some ideas to minimize your app’s download and install size.

1. Upload your app with Android App Bundles

To reduce the size of your app when publishing to Google Play, the most straightforward approach is to upload it as an Android App Bundle. This upload format includes all the compiled code and resources for your app but delays the APK generation and signing process to Google Play.

With this app-serving model, Google Play generates and delivers optimized APKs based on the user’s device configuration. As a result, users only download the code and resources necessary to run your app. You no longer need to create, sign, and manage multiple APKs to support various devices, and users can benefit from smaller, more optimized downloads.

2. Use Multiple APK files

If you can’t create bundles for your application, for example, because you need to upload your app to a store that doesn’t support app bundles, then you can opt for the option of creating multiple APK files.

Using multiple APK files can be a great way to reduce the size of your Android app. When you create multiple APK files, each one can target a specific device configuration or feature set. This allows you to include only the resources and code that are necessary for each specific configuration, reducing the overall size of your app.

By creating multiple APK files, you can also optimize your app for different screen sizes, processor architectures, and other device configurations. This can help ensure that your app runs smoothly and efficiently on a wide range of devices, which can improve the user experience and increase user engagement.

3. Keep your build tools up to date

Using the latest version of the build tools (like Gradle, Android Gradle Plugin, Android Studio, bundle tool, etc) can be a great way to reduce the size of Android APKs. Newer versions often include improvements in performance and optimizations that can significantly reduce the size of the final APK.

One of the key benefits of using the latest tools is that they often include more efficient algorithms and methods for compressing and optimizing code and resources. For example, newer versions of ProGuard and R8, which are used for code shrinking and obfuscation, can significantly reduce the size of the final APK.

4. Keep your libraries up to date

Using the latest library versions can be a great way to reduce the size of your Android APK. The latest versions often have better performance optimizations, bug fixes, and feature improvements that can make your app run more efficiently and smoothly.

Using the latest library versions can also help you reduce the overall size of your app by eliminating the need for additional code or resources. Many libraries have been optimized to reduce their size and minimize their impact on your app’s APK size. By using the latest versions of these libraries, you can take advantage of these optimizations and reduce the size of your app.

5. Enable R8

The Android Gradle Plugin works with the R8 compiler (instead of Proguard since v3.4.0) to handle the following compile-time tasks:

  • Code shrinking (or tree-shaking): detects and safely removes unused classes, fields, methods, and attributes
  • Resource shrinking: removes unused resources
  • Obfuscation: shortens the name of classes and members
  • Optimization: inspects and rewrites your code to further reduce the size of your app’s DEX files

To minimize the size of your app, it is recommended to activate the shrinking feature in your release build. This will eliminate any unused code and resources in your app, leading to a smaller app size. Additionally, enabling shrinking provides the benefits of obfuscation, which shortens the names of your app’s classes and members, and optimization, which utilizes more advanced techniques to further decrease the size of your app.

To activate shrinking, obfuscation, and optimization, you should include the relevant code in your project-level build.gradle.kts file.

android {
buildTypes {
getByName("release") {
// Enables code shrinking, obfuscation, and optimization
isMinifyEnabled = true
// Enables resource shrinking
isShrinkResources = true
// Includes the default ProGuard rules files that are
// packaged with the Android Gradle plugin.
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
}

6. Minimize usage of third-party libraries

When creating an Android application, it is common to utilize external libraries in order to enhance the app’s functionality and flexibility.

In the event that a library was originally designed for use on a server or desktop, it may contain a plethora of objects and methods that are not necessary for the app. In such cases, sometimes is possible to modify the library’s files and only include the relevant portions of the app. Alternatively, a mobile-friendly library can be employed to achieve specific functionality within the app.

It’s also important to try to reduce to the minimum the usage of third-party libraries. A typical issue is having multiple tracking/analytics libraries that sometimes have similar features.

7. Analyze transitive library dependencies

Minimizing the usage of third-party libraries is not enough. You also need to inspect which transitive dependencies are being loaded by each dependency you include in your project.

You can run the following Gradle task to get a tree with all the transitive dependencies of your app, so you can inspect them:

./gradlew app:dependencies

8. Remove unused dependencies

Having unused dependencies declared on your grade configuration files increases your build times and APK size. You can use the gradle-lint-plugin to detect those unused dependencies. Just add the following configuration to your root build.gradle file:

buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.netflix.nebula:gradle-lint-plugin:{VERSION}'
}
}

allprojects {
apply plugin: 'nebula.lint'
gradleLint.rules = ['all-dependency']
}

And then run the lintGradle task. You will see a report with all the unused dependencies, as well as other useful Gradle warnings. Running this report automatically using your CI tool could be a good idea.

9. Remove your unused code

Removing your unused code is a good way to reduce your APK size. It also has other advantages, like avoiding each developer wasting time understanding and compiling it.

On Android Studio you can use the Code -> Code Cleanup tool to find unused code.

Removing your unused code should be one of your main technical priorities.

10. Avoid enumerations

An enum in your app can increase the size of its classes.dex file by approximately 1.0 to 1.4 KB. This size increase can become significant for larger or more complex systems, as well as shared libraries. To mitigate this impact, you may want to consider using the @IntDef annotation and code shrinking techniques to remove enums and replace them with integers. By doing so, you can maintain the type safety benefits of enums while reducing the overall size of your app.

11. 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.

12. 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:

13. 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.

14. 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"))
}
}

15. 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" />

16. 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.

17. 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.

18. 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.

19. 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.

20. 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 }

21. Use nine-patch images

Nine-patch images are a useful tool for reducing the size of Android APKs. These images allow developers to create scalable images that can be resized without losing quality, reducing the need for multiple versions of the same image. This means that fewer images need to be included in the APK, reducing its size and improving performance.

In addition, using nine-patch images can help to improve the user experience of the app. By providing high-quality images that scale well across different devices and screen sizes, developers can ensure that the app looks and performs well on all devices.

However, it’s important to note that nine-patch images are not suitable for all types of images. They work best for images with simple shapes and clear borders, and may not be suitable for more complex images or those with intricate details.

22. Avoid images with text inside

Having images in your APK with text inside could be a bad idea if you don’t take into account i18n (internationalization) and l10n (localization) considerations to ensure a positive user experience for users in different languages and cultures. You need to make sure that the text inside the image is translated and localized to the user’s language.

If the text is part of the image, you are going to need to have one image per user’s language, which is going to increase your APK size. A better approach could be trying to extract the text from the image so you can have a single image and internationalize the text.

23. Use vector graphics

Vector graphics are a great way to create icons and other scalable media that can be displayed at any resolution. By using vector graphics, you can significantly reduce the size of your APK file. In Android, VectorDrawable objects represent vector images. With just a 100-byte file, you can generate a high-quality image that is sharp and clear no matter the size of the screen.

However, rendering each VectorDrawable object takes a considerable amount of time, and larger images take even longer to appear on the screen. Therefore, it is recommended that you only use vector graphics when displaying small images.

If you want to learn more about working with VectorDrawable objects, please refer to the documentation on Working with Drawables.

24. Stream media files

Streaming media files is becoming an increasingly popular method of reducing the size of Android APKs. Instead of including all media files in the APK, developers can choose to stream these files on-demand. This means that the media files are not downloaded until they are needed, reducing the initial download size of the app.

One of the key benefits of streaming media files is that it makes the app more accessible to users with limited bandwidth and storage space on their devices. This is particularly important in emerging markets where many users do not have access to high-speed internet or have limited data plans.

Streaming media files can also make it easier to update and maintain the app. By hosting media files on a server, developers can make changes and updates to the files without having to push out a new version of the app. This means that users can benefit from new features and improvements to the media files without having to download a new version of the app.

In addition, streaming media files can improve the overall performance and user experience of the app. By not including large media files in the APK, the app can load faster and use less memory, improving the performance of the app on older or less powerful devices.

Of course, there are also some challenges and considerations when it comes to streaming media files. Developers need to ensure that the app can handle interruptions in the network connection and that the user experience is not negatively impacted by slow or unreliable connections.

25. Native animated image decoding

The NDK ImageDecoder API in Android 12 (API level 31) now has the ability to decode all frames and timing data from images using animated GIF and animated WebP file formats. In Android 11, this API only decoded the first image from animations in these formats.

By using ImageDecoder instead of third-party libraries, APK size can be further reduced and future updates related to security and performance can be leveraged.

For more information on the API, consult the API reference and the sample provided on GitHub.

26. Remove debug symbols from your native libraries

Debug symbols are helpful during the development stage when debugging is necessary. If your application is still in development, it makes sense to use debug symbols. However, when it comes time to create a release build, you can use the arm-eabi-strip tool, which is available in the Android NDK, to remove any unnecessary debug symbols from your native libraries. Once this is done, you can compile your release build without the debug symbols, which will reduce the size of your app’s binary files.

For more info, you can read here.

27. Avoid extracting native libraries

To optimize the release version of your app, you can package uncompressed .so files in the APK by modifying the useLegacyPackaging flag in your app’s build.gradle file. Setting useLegacyPackaging to false will prevent the PackageManager from copying .so files from the APK to the device’s file system during installation. This approach also has the added benefit of reducing the size of updates for your app.

28. Create on-demand modules

The app serving model of Google Play, known as Dynamic Delivery, utilizes Android App Bundles to create and distribute optimized APKs that match the specific device configuration of each user. By doing so, users only download the necessary code and resources required to run the app, resulting in smaller and more efficient downloads. This approach eliminates the need to create, sign, and maintain multiple APKs to cater to various devices, making the app development process more streamlined.

The benefits of Dynamic Delivery also allow you to modularize app features that aren’t required at install time by adding dynamic feature modules to your app project and including them in your app bundle. Through Dynamic Delivery, your users can then download and install your app’s dynamic features on-demand. Dynamic feature modules allow you to separate certain features and resources from the base module of your app and include them in your app bundle. Through Dynamic Delivery, users can later download and install those components on demand after they’ve already installed the base APK of your app.

For example, consider a text messaging app that includes functionality for capturing and sending picture messages, but only a small percentage of users send picture messages. It may make sense to include picture messaging as a downloadable dynamic feature module. That way, the initial app download is smaller for all users and only those users who send picture messages need to download that additional component.

You can read more here.

29. Use feature modules for specific devices

To reduce the initial download size of your app, you can configure certain features to be downloaded only by devices that support certain capabilities, such as the ability to take pictures or support augmented reality features.

The Conditional delivery allows you to specify certain user device requirements, such as hardware features, locale, and minimum API level to determine whether a modularized feature is downloaded at app install.

30. Analyze your build with the APK Analyzer

Android Studio provides an APK Analyzer that instantly reveals the makeup of your APK or Android App Bundle. Utilizing the APK Analyzer can shorten the time you spend on debugging problems related to DEX files and resources in your app, as well as minimize the size of your APK. The APK Analyzer is also accessible from the command line through apkanalyzer.

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