Reducing mobile download and installed size
Size affects download time, update reliability, storage pressure, and sometimes acquisition. The impact depends on the audience, market, network, and current size. Measure it in your product analytics rather than repeating an old universal conversion statistic or cellular-download limit.
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.
The reduction depends on how much the universal artifact duplicates ABIs, densities, and languages. Build an App Bundle, generate device-specific artifacts with bundletool, and compare what representative devices actually receive. Also verify any non-Play distribution path before changing release output.
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: Supports lossy, lossless, and transparent assets on current platform targets. Compare decoded memory, visual quality, toolchain support, and packaged size against the existing asset rather than assuming one percentage.
- AVIF: Can compress photographic content efficiently on supported targets, but encode time, decode support, alpha behavior, and asset-pipeline compatibility still need testing.
- 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, generate several quality levels and compare the actual screens where the asset appears. A quality number is encoder-specific, and “visually indistinguishable” depends on the image, scaling, and display.
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.
Measure R8's effect on the release artifact and test reflection, serialization, JNI, and dynamically loaded classes. The reduction depends on the dependency graph and keep rules, so preserve mapping files and investigate unexpectedly small or large changes.
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 can be major contributors to binary size, especially when they bundle native frameworks, resources, or several network adapters. Measure the release artifact before and after each dependency. Package names and repository sizes do not tell you what the store will deliver to a specific device.
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.
DEVSFLOW Maintenance includes measured application-size analysis and remediation where it is relevant to the product. Review the standard maintenance scope.