page.title=Reduce APK Size trainingnavtop=true @jd:body
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.
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:
CERT.SF
and
CERT.RSA
signature files, as well as the {@code MANIFEST.MF}
manifest file.
resources.arsc
.
armeabi
, armeabi-v7a
,
arm64-v8a
, x86
, x86_64
, and
mips
.
An APK also contains the following files. Among them,
only AndroidManifest.xml
is mandatory.
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.
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 {@link android.graphics.drawable.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.
The {@code 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 {@code
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 {@code shrinkResources} in
your app's build.gradle
file.
android { // Other settings buildTypes { release { minifyEnabled true shrinkResources true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } }
To use {@code shrinkResources}, you must enable code shrinking. During the build process, first ProGuard removes unused code but leaves unused resources. Then Gradle removes the unused resources.
For more information about ProGuard and other ways Android Studio helps you reduce APK size, see Shrink Your Code and Resources.
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 {@code resConfig} and {@code 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.
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.
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.
For more information screen densities, see Screen Sizes and Densities.
Frame-by-frame animations can drastically increase the size of your APK. Figure 1 shows an example of a frame-by-frame animation separated into multiple PNG files within a directory. Each image is one frame in the animation.
For each frame that you add to the animation, you increase the number of images stored in the APK. In Figure 1, the image animates at 30 FPS within the app. If the image animated at only 15 FPS instead, the animation would require only half the number of needed frames.
Some images don't require a static image resource; the framework can
dynamically draw the image at runtime instead. {@link
android.graphics.drawable.Drawable} objects (<shape>
in
XML) can take up a tiny amount of space in your APK. In addition, XML {@link
android.graphics.drawable.Drawable} objects produce monochromatic images
compliant with material design guidelines.
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 {@code android:tint} and {@code tintMode} attributes on Android 5.0 (API level 21) and higher. For lower versions of the platform, use the {@link android.graphics.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 an "expand" arrow into a "collapse" arrow icon by simply rotating the original image 180 degrees:
<?xml version="1.0" encoding="utf-8"?> <rotate xmlns:android="http://schemas.android.com/apk/res/android" android:drawable="@drawable/ic_arrow_expand" android:fromDegrees="180" android:pivotX="50%" android:pivotY="50%" android:toDegrees="180" />
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.
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:
aapt
tool does not shrink PNG files contained in the
asset/
folder.
aapt
tool to optimize them.
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 }
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 image quality.
The {@code 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.
For JPEG files, you can use tools like packJPG that compress JPEG files into a more compact form.
Instead of using PNG or JPEG files, you can also use the WebP file format for your images. The WebP format provides lossy compression (like JPEG) as well as transparency (like PNG) but can provide better compression than either JPEG or PNG.
Using the WebP file format has a few notable drawbacks, however. First, support for WebP is not available in versions of the platform lower than Android 3.2 (API level 13). Second, it takes a longer amount of time for the system to decode WebP than PNG files.
Note: Google Play accepts APKs only if the included icons use the PNG format. You can't use other file formats like JPEG or WebP for app icons if you intend to publish your app through Google Play.
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 {@link android.graphics.drawable.VectorDrawable} objects. With a {@link android.graphics.drawable.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 {@link android.graphics.drawable.VectorDrawable} object, and larger images take even longer to appear on the screen. Therefore, consider using these vector graphics only when displaying small images.
For more information on working with {@link android.graphics.drawable.VectorDrawable } objects, see Working with Drawables.
There are several methods you can use to reduce the size of the Java and native codebase in your app.
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.
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.
If your app uses native code and the Android NDK, you can also reduce the size of your app by optimizing your code. Two useful techniques are removing debug symbols and not extracting native libraries.
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.
Store {@code .so} files uncompressed in the APK, and set the {@code
android:extractNativeLibs} flag to false in the {@code
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.
For more information, see Configure APK Splits and Maintaining Multiple APKs.