How to Reduce Android APK file size
There are several reasons you, as a developer, should be
concerned about the size of your application and be actively trying to reduce
APK size as much as possible. For one thing, there are potential users of your
application who might be limited by their data plan, connectivity, or storage
space on their phones. Offering an application with size of tens or
hundreds of megabytes could potentially make them think twice before
downloading your app and maybe even go for a different option just to save
the bandwidth.
First of all, let’s describe the APK file format and analyze
what’s hides inside of it.
APK file format
APK (Android application package) is the package file format for
distribution and installation of Android mobile apps. It contains all of the
application’s code, resources, assets, manifest file and other application’s
files. APK is a type of archive file, specifically it is based on the JAR and
ZIP file formats.
Application size
When thinking of the APK size, most people
think of the file size of the APK generated by Android Studio. This is
the raw APK size. Although this is technically correct, there
are other aspects one should take into consideration concerning the size of
your Android application.
When users download your application from
Google Play, they get the APK generated by your IDE. The APK however contains
some of the files uncompressed. This is intentional, and in certain cases even
saves space. For example, shared libraries can stay inside of the APK file and
be mapped to the memory directly when uncompressed. It also helps the Google
Play delta algorithm to produce smaller diffs. Google Play however gzip
compresses the whole APK file before it’s downloaded, so bandwidth is saved.
This is the download size.
Another important size factor is the size
of installed application. The total amount of storage space your
application consumes consists of the raw APK size, *.oat files (pre-compiled
byte code to native code), uncompressed native libraries (this is optional on
Android M+), and also files created at runtime (cache, images, database files,
etc.).
APK contents
In order to reduce APK size of your
application, first you need to know precisely what the APK archive consists of.
As the APK file format builds upon ZIP file format, it can be easily extracted
using for example command unzip:
unzip app-release.apk
The
content of the archive is in most cases this:
- AndroidManifest.xml – this file is a binary
representation of your application manifest. Google Play uses information
from it to decide whether the application can be installed on given
devices or not;
- assets/ – this folder contains
all the assets you’ve included into your application;
- classes.dex – contains compiled
application source code into bytecode. There might be more than one of
those files, depending on the number of methods in your code. From
Android M+, these are compiled into OAT files by the ahead-of-time compiler
at install time;
- libs/ – this folder contains
any included native libraries divided into folders by ABI (CPU
architecture);
- META-INF/ – this folder is present
in a signed APK and contains a list of all files in the APK
together with their signatures;
- res/ – this folder contains
most XML resources (in binary form) and drawables;
- resources.arsc – file containing
flattened and compiled identifiers and other resources. It is stored
uncompressed, so that the content can be mapped into memory directly.
Reduce APK size: Graphics resources
After
a bit of theory, let’s get our hands dirty and start optimizing. We’ll begin
with graphics resources which are usually the most storage-demanding part of
the Android application. Don’t forget that it’s not only the image resources
bundled with your application that matter. If you optimize graphics downloaded
from the server (such as profile pictures, article photos, etc.), you save your
users a considerate amount of bandwidth.
Optimize PNG and JPG files
There
is probably no Android application not using graphics resources, even if
it’s just a few icons. These graphic files add to the APK size when it’s built.
So it’s worth the extra effort to shrink the size of the graphics as much as
possible.
For PNG files, there are a lot of utilities
that optimize the files without losing image quality. To name a few examples: pngcrush, pngquant,
or zopflipng might
be good candidates to try out. Using them can make your PNG files size
tens of percents smaller.
For JPG files, there’s for example packJPG tool
that compresses them into a more compact form.
It is worth noting that Android tools might
in fact increase the size of your optimized graphics as they don’t use such
advanced methods of compression. If you optimize your graphics manually, it
might be a good idea to disable the built-in cruncher in the build.gradle file:
android {
aaptOptions {
cruncherEnabled = false
}
}
WebP
Another
great way of reducing the storage requirements for your graphics is to
convert them to a WebP format. It is suitable for replacing both JPG and PNG
files and often produces much smaller images.
Being
quite a new format though, it is only supported in Android 4.0+. Newer features
(such as transparency and lossless compression) are supported since Android
4.2.1+ only.
Vector drawables
Since Android SDK v21, Android supports a new
type of drawable called VectorDrawable.
They are similar to SVG vector format and can be converted from SVG using
Android Studio. Replacing bitmaps with vector graphics when appropriate can
bring huge savings in storage size.
If you want to support older Android
releases, AppCompat makes this available since version 23.2. Using VectorDrawableCompat allows to bring vector drawables back
to Android SDK 7+.
Shape drawables
One of
the not very often used Android features that has been available in the
framework since the beginning is the shape drawable. Using XML, simple shapes
can be created, like rectangles, ovals, lines and rings. Available shape
parameters are for example gradients, rounded corners, stroke effects, etc.
An
example of a shape drawable is this background with gradient and rounded
corners for an ImageView.
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:angle="45"
android:endColor="#245ccc"
android:startColor="#ff0000" />
<padding
android:bottom="6dp"
android:left="6dp"
android:right="6dp"
android:top="6dp" />
<corners android:radius="8dp" />
</shape>
Reusing resources
Reusing graphics resources instead of
providing every possible variant as a separate bitmap could help you reduce APK
size of your application too. For example, Android provides several tools to
help you re-colour an image. On Android L+ it is the android:tint parameter,
while on older versions you can use the ColorFilter class.
Another great example how to save precious
space is to reuse appropriate images with the help of RotateDrawable. For
example an arrow heading the opposite direction from the original could be
created like this:
<?xml version="1.0" encoding="utf-8"?><rotate xmlns:android="http://schemas.android.com/apk/res/android"android:drawable="@drawable/arrow_up"android:fromDegrees="180"android:pivotX="50%"android:pivotY="50%"android:toDegrees="180" />
Shrinking resources
Sometimes you find yourself forgetting to
remove a no longer resource from your application resources directory. This
forgotten item is however still distributed with your application. To
analyze and strip the potential resources that are never included in your
layouts, drawables, or code, add the following line to your gradle.build file:
android { buildTypes { release { minifyEnabled true shrinkResources true } } }
ProGuard
The ProGuard tool
shrinks, optimizes, and obfuscates your code by removing unused code and
renaming classes, fields, and methods with semantically obscure names.
Code shrinking detects and
removes unused classes, methods, fields, etc. from your APK. This also works
for code from included libraries. One of the added benefits is that by
doing this, it helps you to avoid the 64k methods limit. Also, ProGuard
optimizes your code and removes unused code instructions.
To enable code shrinking with ProGuard, make sure to have the
following lines in your build.gradle file:
android { buildTypes { release { minifyEnabled true proguardFiles getDefaultProguardFile(‘proguard-android.txt'), 'proguard-rules.pro' } } ... }
Also, if you want to have your unused resources removed by ProGuard and even further reduce APK size, add this line:
shrinkResources true
Dependency analysis
The ability to use libraries in
your Android projects vastly improves the quality of code and speed of
development. However, as your project grows, you keep adding new and new
libraries starting with the Android Support library, Google Play services, an
image loading library, a HTTP client library, etc.
Soon, you might hit the 64k
methods limit and have to set up your project to use multidex. ProGuard might
be able to help you with this but first, let’s look at some tips on how to
analyze and maybe get rid of libraries and their dependencies that you probably
don’t need anyway. This might help you in your efforts to reduce APK size of
your application even further.
Analyze dependencies
Sometimes, after adding a single small library dependency, your
methods count and application size might increase suddenly. That might happen
because of transitive dependencies which you cannot normally see in your build.gradle file.
Luckily, there’s an easy way of finding out what dependencies
each of your linked libraries use. To do that, execute gradlew command
in your project’s root with arguments in format <modulename>:dependencies, for
example like this:
./gradlew app:dependencies
ClassyShark
ClassyShark is
a standalone tool for Android developers allowing to browse any Android
executable and show information about it including class interfaces and
members, dex counts and dependencies. Also, ClassyShark can be useful in
showing the exact count of methods for each package, which might help you
identify the packages that make your application hit the 64k methods limit.
Working with ClassyShark is straight-forward and there’s also an
easy-to-follow user
guide.
Final tips on working with libraries
Let me present the last two
tips regarding working with libraries in your Android projects which might help
you reduce the APK size.
The first tip is focused on
developers using the Google Play services functionality. It is often the case
that you don’t use the whole set of Google Play functionality in your app, but
only a part of it. For example you might only use Google Maps and Google
Analytics.
So instead of pulling the whole package of
Google Play services, include only specific parts of it that you’re actually
using. There is a convenient list of all available individual API packages in
the user guide.
The second tip is simple in its nature. If you find yourself including a
library and eventually using only a very small part of it (e.g. a single
utility class or even method), consider pulling the particular part of the
library out by copying the source code of what you’re interested in. This
of course has to be done with respect to the library licence and only if the
source code is available.
Comments
Post a Comment