Reducing Mobile App Binary Size Without Losing Features
Binary size matters more than most teams realize. Google's own research shows that for every 6 MB increase in APK size, install conversion rates drop by 1%. Apple imposes a 200 MB limit for cellular downloads. In emerging markets where storage space is limited and data is expensive, a bloated app gets uninstalled first when the user's phone runs out of space.
The good news is that most apps carry significant unnecessary weight. Unused resources, unoptimized images, debug symbols shipped in release builds, and bloated third-party SDKs all contribute. Here is a systematic approach to reducing binary size on both Android and iOS without removing any user-facing functionality.
Measure Before You Cut
Before making any changes, establish a baseline and understand where the size is coming from. Cutting blindly leads to broken features and wasted effort on areas that do not matter.
Android: APK Analyzer and bundletool
Android Studio's APK Analyzer (Build > Analyze APK) breaks down your APK into its component parts: DEX files, resources, native libraries, and assets. Sort by size to see exactly what is consuming space. The classes.dex file contains your compiled code and all dependencies. The res/ folder contains your layouts, drawables, and other XML resources. The lib/ folder contains native .so libraries, which are often the largest single contributor.
For more granular analysis, use bundletool build-apks to generate device-specific APKs from your App Bundle and measure what an actual user downloads. The universal APK size is misleading because no single user downloads the universal build.
iOS: Xcode App Size Report
In Xcode, archive your app and select "Distribute App" with the "App Thinning" option. Choose "All compatible device variants" to generate a size report. This report shows the compressed and uncompressed size for each device type. The App Thinning Size Report breaks down the binary into the executable, frameworks, asset catalogs, and other resources.
You can also use xcrun altool --validate-app or examine the .ipa file directly by renaming it to .zip and inspecting its contents.
Android App Bundles vs. APK
If your Android app still ships as a monolithic APK, switching to the Android App Bundle (AAB) format is the single highest-impact change you can make. Google Play generates optimized APKs for each device configuration, stripping out resources for screen densities, CPU architectures, and languages that the specific device does not need.
A typical app sees a 20% to 40% reduction in download size from this switch alone. The migration is straightforward: change your build output from APK to AAB in your build.gradle and upload the .aab file to Play Console instead of the .apk. Google Play handles the rest.
One caveat: if you distribute outside the Play Store (direct APK downloads, alternative stores), you will still need to generate APKs. Use bundletool build-apks --mode=universal for a single APK, or generate configuration-specific APKs for distribution.
iOS App Thinning
Apple's App Thinning consists of three technologies: slicing, bitcode (now deprecated as of Xcode 14), and on-demand resources. Slicing is automatic: the App Store generates device-specific variants that exclude assets for other screen scales and architectures. An iPhone user does not download iPad-only assets, and a device running on arm64 does not receive armv7 code.
On-demand resources (ODR) let you host assets on the App Store and download them only when needed. This is useful for apps with large asset catalogs, such as games with level-specific textures or education apps with downloadable content packs. Tag resources in your asset catalog with on-demand resource tags, and use the NSBundleResourceRequest API to fetch them at runtime.
Image and Asset Optimization
Images are often the largest category of assets in a mobile app. Optimizing them provides substantial returns.
Format Selection
- WebP: Supported on Android since API 14 and iOS since iOS 14. WebP provides 25% to 35% smaller files than PNG with equivalent quality, and supports transparency. For most apps targeting modern OS versions, WebP is the best default choice for raster images.
- AVIF: Newer format with even better compression than WebP (typically 30% to 50% smaller than JPEG at equivalent quality). Android supports AVIF since API 31 (Android 12). iOS added AVIF support in iOS 16. Use it if your minimum OS targets allow.
- Vector drawables (Android) and PDF/SVG assets (iOS): For icons, illustrations, and simple graphics, vector formats scale to any resolution from a single small file. On Android, use
VectorDrawableXML. On iOS, use PDF assets in the asset catalog or SVG (supported since iOS 13).
Resolution and Compression
Audit your image assets for unnecessarily large dimensions. A background image at 4000x3000 pixels that gets displayed at 1080x1920 is carrying three times more data than needed. Resize images to match their maximum display size, accounting for screen density.
For photographs and complex images, tune compression quality. JPEG quality of 80% is visually indistinguishable from 100% for most content but produces significantly smaller files. Tools like pngquant (lossy PNG compression) and mozjpeg can reduce existing assets without visible quality loss.
Code Shrinking with ProGuard and R8
On Android, R8 (the successor to ProGuard) removes unused code, renames classes and methods to shorter names, and optimizes bytecode. Enable it in your release build type:
- Set
minifyEnabled truein your release build type configuration. - Set
shrinkResources trueto remove unused resources (layouts, drawables, strings) that are not referenced from code. - Maintain your ProGuard rules file carefully. Overly broad
-keeprules defeat the purpose of shrinking. Common culprits are rules like-keep class com.example.** { *; }that prevent any shrinking of your own code.
R8 typically reduces DEX file size by 10% to 30%. The combination of code shrinking and resource shrinking can have a dramatic effect, especially in apps with large dependency trees where many library classes go unused.
On iOS, the compiler performs dead code stripping automatically when Link-Time Optimization (LTO) is enabled. Check your build settings to ensure "Dead Code Stripping" is set to Yes and "Link-Time Optimization" is set to "Incremental" for release builds.
Removing Unused Resources
Over time, apps accumulate resources that are no longer referenced: old icons from a previous redesign, strings for removed features, layouts for screens that no longer exist. These dead resources add weight without value.
On Android, shrinkResources true handles this automatically in release builds. For a manual audit, Android Studio's "Remove Unused Resources" refactoring (Refactor > Remove Unused Resources) identifies unreferenced resources. Run it, review the results carefully (some resources are loaded dynamically by name and will be missed by static analysis), and delete what is truly unused.
On iOS, there is no built-in equivalent. Tools like FengNiao or LSUnusedResources scan your project for image assets that are not referenced in code or storyboards. Manual review is still important because some assets are referenced dynamically using string interpolation.
Dynamic Feature Modules (Android)
For larger apps, Android's dynamic feature modules let you split functionality into modules that are downloaded on demand rather than included in the initial install. Users who never use a specific feature never download the code and assets for it.
Good candidates for dynamic feature modules include: onboarding flows that run once, advanced editing tools used by a subset of users, region-specific features, and diagnostic or debug tools. The Play Core library provides the SplitInstallManager API for requesting and managing module downloads at runtime.
The tradeoff is added complexity in your build configuration and runtime code. Each dynamic module needs its own Gradle module, manifest, and resource set. You also need to handle the case where a user tries to access a feature whose module has not yet been downloaded.
Auditing Third-Party SDKs
Third-party SDKs are frequently the largest contributors to binary size, and teams often do not realize how much weight they add. A single analytics SDK can add 2 to 5 MB. An ad mediation SDK with multiple ad network adapters can add 10 MB or more.
Review every SDK in your dependency list and check its size contribution using APK Analyzer or the Xcode size report. Ask whether each SDK is still needed, whether a lighter alternative exists, and whether you are using enough of its functionality to justify the size cost. Sometimes replacing a full SDK with a few direct API calls removes megabytes from the binary.
Binary size is not a one-time fix. It is a maintenance discipline. Set a size budget, track it in CI, and treat size regressions the same way you treat performance regressions: investigate, understand, and address them before they accumulate.
App size optimization is one of the ongoing maintenance tasks that DEVSFLOW handles for the apps we manage. Learn about our maintenance plans and how we keep production apps lean and fast.