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

Popular posts from this blog

Carousel view in Android

Get Phone Contacts and display in Listview in Android

Draw Pie Chart using Canvas and Third party Library in Android