Home Introduction How to reduce APK size by 50 percent

How to reduce APK size by 50 percent

by pratheep kanati

Reduce Apk Size:

Apk Size Plays a major role in the user’s decision to download the app. This article describes how to reduce the APK size of your app, allowing more users to download the app.

Apk Structure

  • META-INF/: contains the signature files CERT.SF and CERT.RSA and the manifest file MANIFEST.MF.
  • Assets/: Contains the assets of the app that the AssetManager object can retrieve from the app.
  • Res/: Contains resources not compiled into resources.arsc .
  • Lib/: Includes a compiled code specific to a processor’s software layer. For each platform type, this directory contains a subdirectory, such as armeabi, armeabi-v7a, arm64-v8a, x86, x86 64 and mips. .

The following files are also included in an APK. Only AndroidManifest.xml is compulsory among them.

  • Resources.arsc: includes resources compiled. This file contains the content of XML from all res / values / folder configurations. This XML content is extracted from the packaging tool, compiled into binary form, and archived content. This content includes language strings and styles, as well as content paths that are not directly included in the resources.arsc file, such as layout files and images..
  • Classes.dex: contains the classes compiled by the Dalvik / ART virtual machine in the DEX file format.
  • AndroidManifest.xml: contains the manifest file core for Android. This file lists the app’s name, version, access rights, and library files referenced. The file uses the XML binary format of Android.

Project structure:

Android Studio Project Structure

Reduce resource count and size

Your APK’s size affects how quickly your app loads, how much memory it uses, and how much power it consumes. One of the simple ways of reducing your APK is by reducing the number and size of the resources it contains. In particular, you can remove resources your app no longer uses, and instead of image files you can use scalable Drawable objects. This section discusses these methods and a number of other ways to reduce the resources in your app to reduce the overall size of your APK..

In This  Tutorial We can Reduce Apk Size By 50 percent By  spliting Apk By  Using Proguard,Spliting Apk By ARM and Screen Size


proguard is the tool to remove unused code from the application thats Reduce the apk size to some extent.The following is Implementation of Proguard in app build.gradle file .

apply plugin: 'com.android.application'
android {
buildTypes {
release {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
debuggable false
jniDebuggable false
renderscriptDebuggable false
pseudoLocalesEnabled false
zipAlignEnabled true
view raw build.gradle hosted with ❤ by GitHub

Common Proguard Rules

-optimizationpasses 5
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
-keep class com.android.**
-keep class com.google.android.**
-keep class com.google.android.gms.**
-keep class com.google.android.gms.location.**
-keepattributes Exceptions, InnerClasses, Signature, Deprecated, SourceFile,LineNumberTable, *Annotation*, EnclosingMethod
-keepclassmembers class * implements java.io.Serializable {
private static final java.io.ObjectStreamField[] serialPersistentFields;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
-keep class com.google.android.gms.common.api.GoogleApiClient {
void connect();
void disconnect();
-keep public interface android.app.OnActivityPausedListener {*;}
-keep class com.onesignal.ActivityLifecycleListenerCompat** {*;}
-keep class android.support.v7.widget.SearchView {
public (android.content.Context);
public (android.content.Context, android.util.AttributeSet);
-keep interface android.support.v7.widget.SearchView {
public (android.content.Context);
public (android.content.Context, android.util.AttributeSet);
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
-keep public class com.android.vending.licensing.ILicensingService
-keepclasseswithmembernames class * {
-keepclasseswithmembers class * {
public (android.content.Context, android.util.AttributeSet);
-keepclasseswithmembers class * {
public(android.content.Context, android.util.AttributeSet, int);
-keepclassmembers class * extends android.app.Activity {
public void *(android.view.View);
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
view raw proguard-rules.pro hosted with ❤ by GitHub

Above progaurd rules add to the proguard-rules.pro in root of your project.Add Extra Rules According to the your requirement to remove un used classes and codes.

Reducing APK size by using ABI Filters and APK split

Native libraries are becoming increasingly popular nowadays. They are created primarily for reasons of performance, security and portability across platforms. Unity, Realm, and OnFido are popular examples.

Native libraries are compiled directly into native processor (CPU) code as opposed to Android applications running on Android Virtual Machine (Dalvik or Android Runtime(ART)). This code varies depending on the processor-supported architecture (set of instructions). The Android platform supports multiple architectures of processors:

  • mips
  • mips64
  • armeabi
  • armeabi-v7a
  • arm64-v8a
  • x86
  • x86_64

We need to provide different compilations for each of the CPU architectures that we want to support if we want to use native libraries in our application. We have one or more separate.so files with native code for each architecture. :

Native libraries in Normal Apk File

Our APK (Application PacKage) file size can grow like crazy when we decide to support CPU architectures. When a user downloads the app, he will get all of them, despite only needing the set for his device architecture.

Let’s use APK Analyzer (which comes with Android Studio) and check what’s going on inside our APK with native libraries by clicking in android studio apk.

Final Apk Structure of normal Apk File
Final Apk Structure of normal Apk File

Looking closer, we see how much space native libraries consume for each architecture

The Seeing Above We  Understand That  More than 50 percent of Apk Occupied by the Native Libraries

Only by our code and resources remaining space. Therefore we need to limit our APK to support certain architectures in order to decrease APK size. There are two techniques we should take into account :

  • ABI Filters
  • APK Split

ABI Filters

ABI (Application Binary Interface) is often an interface between two program modules, one of which is a library or an operating system.

ABI filters enable us to filter certain architecture(s) we want to include in a single APK file.

buildTypes {
release {
ndk {
abiFilters arm64-v8a,armeabi-v7a;
view raw build.gradle hosted with ❤ by GitHub

By removing unnecessary architectures, we can create a single, much smaller-sized APK file (note that there are not many devices that support Android’s x86 architecture, but the largest libraries for this architecture).

This solution has advantages and disadvantages, so let’s consider two perspectives — user perspective and developer perspective

From the user perspective, APK contains more libraries than user needs (two architectures rather than one), which means a larger size of APK. Ultimately, the user will use more data from the mobile network and wait longer to download the installation application. This may reduce UX in general.

Keep in mind that mobile data transfer is quite expensive in some emerging markets (such as India). Users often use Bluetooth or Hot-Spot to share the application via SHAREit or Xender (these apps have users around 100-150 M). If we downloaded app that contains native libraries only for arm64-v8a architecture it will not also be working when we share with the device supporting armeabi-v7a after sharing the APK file (other ways around this would work because arm-v8a is backward compatible)

This solution provides a single APK from a developer perspective that can save us a lot of time. This means no additional overhead related to build process configuration and APK testing and maintenance (read about multiple APK support here).

If we use a single library that adds just a few MBs for each architecture then the overall “user cost” may be small, and our effort may not be worth multiple APKs. However, if we use multiple native libraries, the total library size can be quite large for each platform:

APK split

Apk split allows multiple APK files to be generated. The screen density (mdpi, hdpi, xhdpi …) or architecture (arm64-v8a, armeabi-v7a …) can be split. Let’s set up architecture split:

android {
splits {
// Configures multiple APKs based on ABI.
abi {
// Enables building multiple APKs per ABI.
enable true
// By default all ABIs are included, so use reset() and include to specify that we only
// want APKs for x86, armeabi-v7a, and mips.
// Specifies a list of ABIs that Gradle should create APKs for.
include 'x86_64', 'x86', 'armeabi', 'armeabi-v7a', 'arm64-v8a'
// Specifies that we want to also generate a universal APK that includes all ABIs.
universalApk true
view raw build.gradle hosted with ❤ by GitHub

One APK file per each architecture will be generated and (optionally) one universal file containing all architectures.

Generated Apk Files After Sign Apk

If we also set up screen density splits, then APK files would be generated for all combinations of density+architecture (app-uk-mdpiArmeabi-v7a-debug.apk, app-uk-mdpiArm64-v8a-debug.apk, app-uk-hdpiArmeabi-v7a-debug.apk, app-uk-hdpiArm64-v8a-debug.apk, etc.).
Splitting may be unnecessary for debugging purposes, so we can only configure splits for our release builds. There are few ways this can be approached. We can pass a Gradle build script parameter to state explicitly that this build should be split. This is a way to go if you want to be able to enable or disable splitting on your build server. However, we can also configure splits to build by retrieving the name of the Gradle task while the task is running . This will enable us to determine whether the current Gradle task is a task that creates a release build (e.g. assembly Paid Release) and decide whatever we want to allow APK to be split or not :

android {
splits {
abi {
def isReleaseBuild = false
gradle.startParameter.taskNames.find {
// Enable split for release builds in different build flavors
// (assemblePaidRelease, assembleFreeRelease, etc.).
if (it ==~ /:app:assemble.*Release/) {
isReleaseBuild = true
return true // break
return false // continue
// Enables building multiple APKs per ABI.
enable isReleaseBuild
view raw build.gradle hosted with ❤ by GitHub

Now running assembleRelease tasks generates multiple APK files split by CPU architecture for each flavor, while running assembleDebug tasks generates single APK file.

Version codes:

Google Play Store does not allow multiple version code APK files to be uploaded. We must ensure that each APK has a unique versionCode of its own.

import com.android.build.OutputFile
android {
splits {
// Map for the version code that gives each ABI a value.
def abiCodes = ['x86':1, 'x86_64':2, 'armeabi-v7a':3, 'arm64-v8a':4]
// APKs for the same app that all have the same version information.
android.applicationVariants.all { variant ->
// Assigns a different version code for each output APK.
variant.outputs.each {
output ->
def abiName = output.getFilter(OutputFile.ABI)
output.versionCodeOverride = abiCodes.get(abiName, 0) * 100000 + variant.versionCode
view raw build.gradle hosted with ❤ by GitHub

This code will allows us to override version code for each APK

universal: 1, 2, 3
x86: 100001, 100002, 100003
x86_64: 200001, 200002, 200003
armeabi-v7a: 300001, 300002, 300003
arm64-v8a: 400001, 400002, 400003
view raw Versioning hosted with ❤ by GitHub

We could also update the version name (e.g. by modifying additional versionNameOverride property in the previous code to reflect current architecture. We only want to do this if abiName is not null (we want to leave the universal APK name intact)

Which architectures to support

We definitely need to support armeabi-v7a and arm64-v8a architectures because they have about 99% of the propagation of the market. There’s an interesting case with x86/x86 64. They are insignificant from the mobile market perspective, and this is unlikely to change in the future. However, Google is pushing developers to make their Android apps compatible with Chromebooks, mostly using x86/x86 64 architecture-based Intel processors (meaning we can consider adding support for it).

Most Chromebooks have Intel processors that support Intel ® VT-x, enabling them to run HAXM (the same technology we use to make apps run much faster in our emulators), so the emulation of the app is pretty fast.

To summarize here is a complete list of Android architectures with recommendations for use

  • mips (deprecated)
  • mips64 (deprecated)
  • armeabi (deprecated)
  • armeabi-v7a (required — most popular architecture nowadays)
  • arm64-v8a (required — newer version of armeabi-v7a)
  • x86 (optional, very limited number of devices, may be useful for debugging in the emulator)
  • x86_64 (optional, very limited number of devices, may be useful for debugging in the emulator)


There are no golden rules here, as we can see. The solution we might need depends on the size of our native libraries, the ability to configure and manage the build / release process properly, and the market we want to target .

We should look at the size of our native libraries before making a decision about using ABI filters or APK splitting, check our user base and determine how many users will actually benefit from this change. We should also consider our side’s overall cost.

You may also like

Leave a Reply