APK Structure#
Before discussing how to reduce the size of an application, it is helpful to understand the structure of an application APK. An APK file consists of a Zip compressed file that contains all the files that make up the application. These files include Java class files, resource files, and files containing compiled resources.
An APK contains the following directories:
META-INF/
: Contains theCERT.SF
andCERT.RSA
signature files, as well as theMANIFEST.MF
manifest file.assets/
: Contains the application's resources; the application can retrieve these resources using the AssetManager object.res/
: Contains resources that are not compiled intoresources.arsc
.lib/
: Contains compiled code specific to the processor software layer. This directory contains subdirectories for each platform type, such asarmeabi
,armeabi-v7a
,arm64-v8a
,x86
,x86_64
, andmips
.
The APK also contains the following files. Among these files, only AndroidManifest.xml
is required.
resources.arsc
: Contains compiled resources. This file includes the XML content from all configurations in theres/values/
folder. The packaging tool extracts this XML content, compiles it into binary form, and archives the corresponding content. This content includes language strings and styles, as well as paths to content (such as layout files and images) that are not directly included in theresources.arsc
file.classes.dex
: Contains classes compiled in the DEX file format understandable by the Dalvik/ART virtual machine.AndroidManifest.xml
: Contains the core Android manifest file. This file lists the application's name, version, permissions, and referenced library files. The file uses Android's binary XML format.
Reducing Resource Count and Size#
The size of the APK affects the application's loading speed, the amount of memory used, and the power consumed. One simple way to reduce APK size is to decrease the number and size of the resources it contains. Specifically, you can remove resources that the application no longer uses and replace image files with scalable Drawable objects. This section will discuss these methods and several other ways to reduce resources in the application to decrease the total APK size.
Removing Unused Resources#
The lint tool is a static code analyzer that comes with Android Studio and can detect resources in the res/
folder that are not referenced by code. When the lint
tool finds potentially unused resources in the project, it displays a message, as shown in the example below.
res/layout/preferences.xml: Warning: The resource R.layout.preferences appears to be unused [UnusedResources]
Note: The lint
tool does not scan the assets/
folder, resources referenced via reflection, or library files linked to the application. Additionally, it does not remove resources; it only alerts you to their presence.
The libraries you add to your code may contain unused resources. If you enable shrinkResources in your application's build.gradle
file, Gradle can automatically remove resources on your behalf.
android {
{
release {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
To use shrinkResources
, you must enable code shrinking. During the build process, first, ProGuard removes unused code but retains unused resources. Then, Gradle removes unused resources.
For more information about ProGuard and other ways Android Studio helps you reduce APK size, see Shrink Code and Resources.
In Android Gradle Plugin version 0.7 and higher, you can declare the configurations supported by your application. Gradle passes this information to the build system using the resConfig
and resConfigs
styles and the defaultConfig
option. Subsequently, the build system prevents resources from unsupported configurations from appearing in the APK, thus reducing the APK size. For details on this feature, see Remove Unused Alternate Resources.
Reducing Resource Usage in Libraries#
When developing Android applications, you often need to use external libraries to enhance the application's usability and functionality. For example, you can reference the Android Support Library to improve the user experience on older devices, or you can use Google Play Services to retrieve automatic translations of text in the application.
If a library is designed for server or desktop devices, it may contain many objects and methods that the application does not need. To include only the parts of the library that your application requires, you can edit the library's files (if the corresponding license allows you to modify the library). You can also use other mobile-friendly libraries to add specific functionality to your application.
Note: ProGuard can clean up some unnecessary code imported with libraries, but it cannot remove large internal dependencies of the library.
Supporting Only Specific Densities#
Android supports a wide range of devices (with various screen densities). In Android 4.4 (API level 19) and higher, the framework supports various densities: ldpi
, mdpi
, tvdpi
, hdpi
, xhdpi
, xxhdpi
, and xxxhdpi
. Although Android supports all these densities, you do not need to export rasterized resources to every density.
If you know that only a small portion of users have devices with a specific density, consider whether it is necessary to bundle those densities into your application. If you do not add resources for specific screen densities, Android will automatically scale existing resources that were originally designed for other screen densities.
If your application only needs scaled images, you can save more space by using a single variant of the image in drawable-nodpi/
. We recommend that each application include at least one xxhdpi
image variant.
For details on screen densities, see Screen Sizes and Densities.
Using Drawable Objects#
Some images do not require static image resources; the framework can dynamically draw images at runtime instead. Drawable objects (in XML as <shape>
) occupy a small amount of space in the APK. Additionally, XML Drawable objects generate monochrome images that comply with Material Design guidelines.
Reusing Resources#
You can add separate resources for variants of images, such as versions of the same image with hue adjustments, shadow settings, or rotations. However, we recommend reusing the same set of resources and customizing them at runtime as needed.
Android provides several utilities to change the color of resources, each of which uses the android:tint
and tintMode
attributes on Android 5.0 (API level 21) and higher. For lower platform versions, the ColorFilter class is used.
You can also omit resources that are merely the rotated equivalents of another resource. The following code snippet provides an example that shows how to turn "thumbs up" into "thumbs down" by rotating the image 180 degrees around its center point:
<?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" />
Rendering from Code#
You can also reduce APK size by rendering images programmatically. Programmatic rendering can free up space because you no longer store image files in the APK.
Compressing PNG Files#
The aapt
tool can optimize image resources placed in res/drawable/
through lossless compression during the build process. For example, the aapt
tool can convert true color PNGs that do not require more than 256 colors into 8-bit PNGs using a palette. This results in images of the same quality but with a smaller memory footprint.
Keep in mind that aapt
has the following limitations:
-
The
aapt
tool does not compress PNG files contained in theassets/
folder. -
Image files need to use 256 colors or fewer to be optimized by the
aapt
tool. -
The
aapt
tool may increase the size of already compressed PNG files. To prevent this, you can disable this process for PNG files using thecruncherEnabled
flag in Gradle:aaptOptions { cruncherEnabled = false }
Compressing PNG and JPEG Files#
You can use tools like pngcrush, pngquant, or zopflipng to reduce the size of PNG files without losing quality. All of these tools can decrease the size of PNG files while maintaining visually perceived quality.
The pngcrush
tool is particularly effective: it iterates through PNG filters and zlib (Deflate) parameters, compressing the image using each combination of filters and parameters. It then selects the configuration that produces the smallest compressed output.
To compress JPEG files, you can use tools like packJPG and guetzli.
Using WebP File Format#
If targeting Android 3.2 (API level 13) and higher, you can also use images in the WebP file format (instead of using PNG or JPEG files). The WebP format provides lossy compression (like JPEG) as well as transparency (like PNG), but it can offer better compression than JPEG or PNG.
You can convert existing BMP, JPG, PNG, or static GIF images to WebP format using Android Studio. For details, see Creating WebP Images with Android Studio.
Note: Google Play only accepts APKs if the launcher icon is in PNG format.
Using Vector Graphics#
You can create resolution-independent icons and other scalable media using vector graphics. Using these graphics can significantly reduce the space occupied by the APK. Vector images are represented in Android as VectorDrawable objects. With VectorDrawable objects, a 100-byte file can generate a clear image that matches the screen size.
However, the system takes a considerable amount of time to render each VectorDrawable object, and larger images take longer to display on the screen. Therefore, consider using these vector graphics only for displaying small images.
For details on using VectorDrawable objects, see Using Drawable Resources.
Using Vector Graphics for Animated Images
Do not use AnimationDrawable to create frame-by-frame animations, as this requires adding separate bitmap files for each frame of the animation, which significantly increases the size of the APK.
Instead, you should use AnimatedVectorDrawableCompat to create animated vector drawable resources.
Reducing Native and Java Code#
You can use various methods to reduce the size of Java and native code libraries in your application.
Removing Unnecessary Generated Code
Make sure to understand the space occupied by any automatically generated code. For example, many protocol buffer tools generate excessive methods and classes, which can double or triple the size of the application.
Avoid Using Enums
A single enum can increase the size of the application's classes.dex
file by about 1.0 to 1.4 KB. These size increases can quickly accumulate, resulting in complex systems or shared libraries. If possible, consider using @IntDef
annotations and ProGuard to remove enums and convert them to integers. This type conversion can retain the various safety advantages of enums.
Reducing the Size of Native Binaries
If your application uses native code and the Android NDK, you can also reduce the size of the release version of the application by optimizing the code. Removing debug symbols and not extracting native libraries are two practical techniques.
Removing Debug Symbols#
If the application is in development and still requires debugging, using debug symbols is very appropriate. You can use the arm-eabi-strip
tool provided in the Android NDK to remove unnecessary debug symbols from native libraries. After that, you can compile the release version.
Avoiding Extraction of Native Libraries#
When compiling the release version of the application, you can package uncompressed .so
files in the APK by setting android:extractNativeLibs="false"
in the application element of the application manifest. Disabling this flag prevents the PackageManager from copying .so
files from the APK to the file system during installation, providing the additional benefit of reducing application updates.
Maintaining Multiple Slim APKs#
An APK may contain content that users download but never use, such as resources for other languages or screen densities. To ensure that users receive the smallest download file, you should upload your application using the Android App Bundle to Google Play. By uploading an App Bundle, Google Play can generate and provide optimized APKs tailored to each user's device configuration, so users only need to download the code and resources necessary to run your application. You no longer need to compile, sign, and manage multiple APKs to support different devices, and users can receive smaller, optimized download packages.
If you do not plan to publish the application on Google Play, you can split the application into multiple APKs and differentiate them based on factors such as screen size or GPU texture support.
When users download your application, their devices will receive the correct APK based on the device's capabilities and settings. This way, the device will not receive resources for features that the device does not possess. For example, if a user has an hdpi
device, they do not need the xxxhdpi
resources that you might provide for devices with higher density displays.
For details, see Configuring APK Splits and Maintaining Multiple APKs.
Reducing Native Size#
android {
...
defaultConfig {
...
// Configure the externalNativeBuild block under the defaultConfig node
externalNativeBuild {
// For ndk-build, instead use ndkBuild {}
cmake { // Specify the compilation options for cmake
// Sets optional flags for the C compiler.
cFlags "-fvisibility=hidden -ffunction-sections -fdata-sections"
// Sets optional flags for the C++ compiler.
cppFlags "-fvisibility=hidden -ffunction-sections -fdata-sections -std=c++11"
arguments "-DANDROID_TOOLCHAIN=gcc"// Set to use gcc for compilation; cmake defaults to using clang
}
}
}
buildTypes {...}
}