Reduce the APK size

Users often avoid downloading apps that seem too large, particularly in
emerging markets where devices connect to often-spotty 2G and
3G networks or work on pay-by-the-byte plans. This article describes how to
reduce your app's APK size, which enables more users to download your app.

Understand the APK structure

Before discussing how to reduce the size of your app, it's helpful to
understand the structure of an app's APK. An APK file consists of a ZIP
archive that contains all the files that comprise your app. These files
include Java class files, resource files, and a file containing compiled
resources.

An APK contains the following directories:

META-INF/: Contains the CERT.SF and
CERT.RSA signature files, as well as the MANIFEST.MF
manifest file.

assets/: Contains the app's assets, which the app can retrieve
using an AssetManager object.

res/: Contains resources that aren't compiled into
resources.arsc.

lib/: Contains the compiled code that is specific to the software
layer of a processor. This directory contains a subdirectory for each
platform type, like armeabi, armeabi-v7a,
arm64-v8a, x86, x86_64, and
mips.

An APK also contains the following files. Among them,
only AndroidManifest.xml is mandatory.

resources.arsc: Contains compiled resources. This file contains
the XML content from all configurations of the res/values/
folder. The packaging tool extracts this XML content, compiles it to binary
form, and archives the content. This content includes language strings and
styles, as well as paths to content that is not included directly in the
resources.arsc file, such as layout files and images.

classes.dex: Contains the classes compiled in the DEX file format
understood by the Dalvik/ART virtual machine.

Reduce resource count and size

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. One of the simple ways to
make your APK smaller is to reduce the number and size of the
resources it contains. In particular, you can remove resources
that your app no longer uses, and you can use scalable Drawable objects in place of image files. This
section discusses these methods as well as several other ways that you can
reduce the resources in your app to decrease the overall size of your APK.

Remove unused resources

The lint tool, a
static code analyzer included in Android Studio, detects resources in your
res/ folder that your code doesn't reference. When the
lint tool discovers a potentially unused resource in your
project, it prints a message like the following example.

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

Note: The lint tool doesn't scan the assets/ folder, assets that are referenced via reflection, or library files
that you've linked to your app. Also, it doesn't remove resources; it only
alerts you to their presence.

Libraries that you add to your code may include unused resources. Gradle can
automatically remove resources on your behalf if you enable shrinkResources in
your app's build.gradle file.

In Android Gradle Plugin 0.7 and higher, you can declare the configurations
that your app supports. Gradle passes this information to the build system
using the resConfig and resConfigs flavors and the
defaultConfig option. The build system then prevents resources
from other, unsupported configurations from appearing in the APK, reducing
the APK's size. For more information about this feature, see Remove unused
alternative resources.

Minimize resource use from libraries

When developing an Android app, you usually use external libraries to improve
your app's usability and versatility. For example, you might reference the
Android
Support Library to improve the user experience on older devices, or you
could use Google Play
Services to retrieve automatic translations for text within your app.

If a library was designed for a server or desktop, it can include many
objects and methods that your app doesn’t need. To include only the parts of
the library that your app needs, you can edit the library's files if the
license allows you to modify the library. You can also use an alternative,
mobile-friendly library to add specific functionality to your app.

Note:ProGuard can clean up some
unnecessary code imported with a library, but it can't remove a library's
large internal dependencies.

Support only specific densities

Android supports a very large set of devices, encompassing a variety of
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 don't need to export your rasterized assets to each
density.

If you know that only a small percentage of your users have devices with
specific densities, consider whether you need to bundle those densities into
your app. If you don't include resources for a specific screen density,
Android automatically scales existing resources originally designed for other
screen densities.

If your app needs only scaled images, you can save even more space by having
a single variant of an image in drawable-nodpi/. We recommend
that every app include at least an xxhdpi image variant.

Use drawable objects

Some images don't require a static image resource; the framework can
dynamically draw the image at runtime instead. Drawable objects (<shape> in
XML) can take up a tiny amount of space in your APK. In addition, XML Drawable objects produce monochromatic images
compliant with material design guidelines.

Reuse resources

You can include a separate resource for variations of an image, such as
tinted, shaded, or rotated versions of the same image. We recommend, however,
that you reuse the same set of resources, customizing them as needed at
runtime.

Android provides several utilities to change the color of an asset, either
using the android:tint and tintMode attributes on Android 5.0
(API level 21) and higher. For lower versions of the platform, use the ColorFilter class.

You can also omit resources that are only a rotated equivalent of another
resource. The following code snippet provides an example of turning a "thumb up"
into a "thumb down" by pivoting at the middle of the image and rotating it 180 degrees:

Render from code

You can also reduce your APK size by procedurally rendering your images.
Procedural rendering frees up space because you no longer store an image file
in your APK.

Crunch PNG files

The aapt tool can optimize the image resources placed in
res/drawable/ with lossless compression during the build
process. For example, the aapt tool can convert a true-color PNG
that does not require more than 256 colors to an 8-bit PNG with a color
palette. Doing so results in an image of equal quality but a smaller memory
footprint.

Keep in mind that the aapt has the following limitations:

The aapt tool does not shrink PNG files contained in the
asset/ folder.

Image files need to use 256 or fewer colors for the aapt
tool to optimize them.

The aapt tool may inflate PNG files that have already been
compressed. To prevent this, you can use the cruncherEnabled
flag in Gradle to disable this process for PNG files:

aaptOptions {
cruncherEnabled = false
}

Compress PNG and JPEG files

You can reduce PNG file sizes without losing image quality using tools like
pngcrush, pngquant, or zopflipng. All of these tools can
reduce PNG file size while preserving the perceptive image quality.

The pngcrush tool is particularly effective: This tool iterates over
PNG filters and zlib (Deflate) parameters, using each combination of filters
and parameters to compress the image. It then chooses the configuration that
yields the smallest compressed output.

Use WebP file format

Instead of using PNG or JPEG files, you can also use the WebP
file format for your images, when targeting Android 3.2 (API level 13) and higher.
The WebP format provides lossy compression (like
JPEG) as well as transparency (like PNG) but can provide better compression
than either JPEG or PNG.

Note: Google Play accepts APKs only if the
launcher icons
use the PNG format.

Use vector graphics

You can use vector graphics to create resolution-independent icons and other
scalable media. Using these graphics can greatly reduce your APK footprint.
Vector images are represented in Android as VectorDrawable objects. With a VectorDrawable object, a 100-byte file can
generate a sharp image the size of the screen.

However, it takes a significant amount of time for the system to render each
VectorDrawable object, and larger images
take even longer to appear on the screen. Therefore, consider using these
vector graphics only when displaying small images.

Use vector graphics for animated images

Do not use AnimationDrawable to create frame-by-frame
animations because doing so requires that you include a separate bitmap file for each frame of the
animation, which drastically increase the size of your APK.

Reduce native and Java code

There are several methods you can use to reduce the size of the Java and
native codebase in your app.

Remove unnecessary generated code

Make sure to understand the footprint of any code which is automatically
generated. For example, many protocol buffer tools generate an excessive
number of methods and classes, which can double or triple the size of your
app.

Avoid enumerations

A single enum can add about 1.0 to 1.4 KB of size to your app's
classes.dex file. These additions can quickly accumulate for
complex systems or shared libraries. If possible, consider using the
@IntDef annotation and ProGuard to strip enumerations
out and convert them to integers. This type conversion preserves all of the
type safety benefits of enums.

Reduce the size of native binaries

If your app uses native code and the Android NDK, you can also reduce the
size of the release version of your app by optimizing your code. Two useful
techniques are removing debug symbols and not extracting native libraries.

Remove debug symbols

Using debug symbols makes sense if your application is in development and
still requires debugging. Use the arm-eabi-strip tool, provided
in the Android NDK, to remove unnecessary debug symbols from native
libraries. After that, you can compile your release build.

Avoid extracting native libraries

When building the release version of your app, package uncompressed .so files
in the APK by setting android:extractNativeLibs="false" in the
<application>
element of your app's manifest. Disabling this flag prevents
PackageManager from copying .so files
from the APK to the filesystem during installation and has the added
benefit of making updates of your app smaller.

Maintain multiple lean APKs

Your APK can contain content that users download but never use, like regional
or language information. To create a minimal download for your users, you can
segment your app into several APKs, differentiated by factors such as screen
size or GPU texture support.

When a user downloads your app, their device receives the correct APK based
on the device's features and settings. This way, devices don't receive assets
for features that the devices don't have. For example, if a user has a
hdpi device, they don’t need xxxhdpi resources that
you might include for devices with higher density displays.