Every team has felt it: the APK that started lean and mean now takes five minutes to download, triggers storage warnings, and somehow includes three different image-loading libraries. Bloat creeps in silently — a few extra drawables here, an unused SDK there, a ProGuard rule that stopped working two releases ago. Before you know it, your app is punishing users on slower networks and older devices.
This guide is for the team that doesn't have a dedicated build engineer or a week to audit dependencies. We've distilled the most impactful cuts into a 30-minute checklist. You won't need to rewrite your app or adopt a new architecture. You just need a clear order of operations and the willingness to say no to unused code.
By the end of this checklist, you'll have removed the easy fat — resources, assets, and libraries that serve no current purpose — and set up a simple process to keep bloat from returning. Let's start with why bloat matters beyond just file size.
Who Needs This and What Goes Wrong Without It
If your team ships updates weekly or monthly, and your APK size has grown more than 20% in the last six months, this checklist is for you. Bloat isn't just a storage issue — it affects conversion rates, update adoption, and even crash rates on low-end devices. Google Play's install-time warning at 100 MB is a hard ceiling, but many teams hit performance problems long before that threshold.
Without regular trimming, several things go wrong. First, download times increase disproportionately on 3G and slower Wi-Fi. A 10 MB increase can add 30 seconds to an install on a weak connection, and users abandon downloads. Second, memory pressure rises. Apps with bloated resources often consume more RAM during parsing and rendering, leading to more background kills and cold starts. Third, build times degrade. Every extra library and resource file adds to compilation and packaging time, making the iteration loop slower for everyone.
We've seen teams that ignored bloat for a year end up with APKs that included assets from three different icon sets, two analytics SDKs (one deprecated), and a full copy of a font that was only used in a single error dialog. The worst part? Nobody noticed until a user complained about the app taking up 400 MB of storage. By then, the cleanup required a dedicated sprint.
The good news is that most bloat follows predictable patterns. Once you know where to look, the first cleanup pass takes under 30 minutes. Subsequent passes, done every few releases, take even less time because you catch problems early.
Who Should Skip This Checklist
If your APK is already under 20 MB and you have automated size regression checks in CI, you probably don't need this guide. Similarly, if your app targets only high-end devices on fast networks and you have no size constraints, bloat removal may not be your top priority. But for the rest of us — teams shipping to a diverse user base — this checklist pays for itself in the first release.
Prerequisites and Context to Settle First
Before you start deleting files and stripping libraries, take five minutes to prepare. The goal is to avoid breaking your app or wasting time on false positives. Here's what you need in place.
Baseline Measurement
Record your current APK size (both universal and per-density splits if you use them). Use Android Studio's APK Analyzer or a simple ls -lh on the build output. Note the uncompressed size as well — Play Store compression can mask bloat. Also check the number of drawable resources, DEX methods, and native libraries. These numbers will tell you where the fat is.
Version Control and Branch
Work on a clean branch. You'll be making changes that could break the build or introduce runtime crashes. Having a revert point saves time. Commit your baseline measurements and any config changes before you start trimming.
Tooling Checklist
You need three things: Android Studio (for APK Analyzer and lint), a terminal with apkanalyzer or aapt2, and a build system that supports product flavors (for testing). Optional but helpful: a dependency analysis tool like gradle dependencyInsight or a plugin that visualizes dependency trees. Don't worry if you don't have all of these — the checklist works with just Android Studio and Gradle.
Understanding What's Safe to Remove
Not all unused resources are safe to delete. Some are referenced by libraries via reflection or resource IDs. Others are used in build-time only (like ProGuard rules). The safest approach is to run Lint's "Unused Resources" scan first, but treat its results as candidates, not guarantees. We'll cover verification steps later.
Also, be aware of multi-module projects. Resources in a library module might be used by another module, even if the current module doesn't reference them directly. Lint can detect cross-module usage if you run it on the whole project, but it's not perfect. When in doubt, search for the resource name across the entire codebase.
Core Workflow: The 30-Minute Bloat Removal Process
Set a timer and follow these steps in order. Each step has a clear stopping point — you can pause after any step and still have a working app. The first three steps are the highest impact and should take about 20 minutes total.
Step 1: Remove Unused Resources (10 minutes)
Run Android Studio's Lint inspection: Analyze > Inspect Code > Android > Lint > Performance > Unused resources. Review the list. Pay special attention to drawables, layouts, and strings. For each resource, ask: Is it referenced anywhere in code or XML? If not, delete it. Be cautious with resources that might be used by libraries — check the library's documentation or source. A common culprit is the values- folder with translations for languages you don't support. Remove unused locale folders entirely.
After deleting, rebuild and run the app. Test a few key flows to ensure nothing crashes. If you're short on time, at least verify the app launches and the main activity renders. You can always revert if something breaks.
Step 2: Audit Dependencies (8 minutes)
Open your build.gradle files and look for dependencies that are no longer used or can be replaced. Run ./gradlew dependencies to see the full tree. Common bloat includes: duplicate libraries (e.g., two HTTP clients), large libraries used for a single feature (e.g., a full image editor SDK when you only crop), and debug-only libraries that are included in release builds. Mark each dependency as implementation instead of api if possible — this reduces the transitive exposure.
For each candidate removal, comment it out and rebuild. If the build succeeds, test the app. If it fails, add it back. This is the fastest way to confirm whether a dependency is truly needed.
Step 3: Strip Unused Native Libraries (5 minutes)
Native libraries are often the biggest space hogs. Check your jniLibs or libs folders. Do you need all architectures? If you support only ARM64 devices, remove ARM, x86, and x86_64. If you want to keep compatibility but reduce size, use APK splits or Android App Bundle to deliver only the relevant native libraries per device. Also look for duplicate .so files — sometimes the same library is included under different names or paths.
After cleaning, verify that the app still loads native code correctly. Test on a device that matches the architectures you kept.
Step 4: Optimize Images and Assets (4 minutes)
Run a tool like pngcrush, svgo, or Android Studio's built-in image optimizer. Convert large PNGs to WebP where possible. Remove unused asset variants (e.g., tablet-specific drawables if your app is phone-only). For vector drawables, ensure they are minified and don't include unnecessary paths.
If you use animated GIFs or videos, consider whether they can be replaced with lighter alternatives like Lottie animations or compressed video codecs.
Step 5: Review ProGuard/R8 Configuration (3 minutes)
Check your ProGuard rules for unnecessary -keep statements. Overly broad rules prevent shrinking and obfuscation, leaving dead code in the APK. Remove any rules that were added for debugging or for libraries that are no longer used. Also ensure R8 is enabled and set to full mode. Run a release build and check the size difference.
After these five steps, rebuild your APK and compare the size to your baseline. You should see a reduction of 10–30% typically, sometimes more if you had significant bloat.
Tools, Setup, and Environment Realities
The checklist above works with minimal tooling, but having the right setup can cut the time in half. Here's what we recommend for teams that want to make bloat removal a regular habit.
Essential Tools
- Android Studio APK Analyzer: Built-in, free, and shows you exactly what's taking space. Use it to compare two APKs and see the diff per category.
- Gradle Build Scan: Helps identify slow tasks and dependency bloat. Free for public projects.
- Lint: Already part of Android Studio. Run it regularly as part of your build.
- apkanalyzer: A command-line tool that outputs JSON for scripting. Useful for CI pipelines.
CI Integration
To prevent bloat from returning, add a size regression check to your CI pipeline. Tools like apkanalyzer can extract the APK size and compare it to a baseline. Set a threshold (e.g., 5% increase) and fail the build if exceeded. This forces teams to justify every addition.
You can also integrate Lint's unused resource check into your build with lintOptions { abortOnError true }, but be careful — some false positives may block releases. A better approach is to run it as a warning and review periodically.
Environment Gotchas
Different build types (debug vs. release) can have vastly different sizes. Always measure on a release build with ProGuard/R8 enabled. Also be aware that Android App Bundle (AAB) sizes reported in Play Console are not directly comparable to APK sizes — the AAB is a publishing format, and the actual APK delivered to devices is smaller. For consistency, measure the universal APK or the split APK that matches your test device.
If your team uses multiple product flavors, each flavor may have different bloat patterns. Run the checklist per flavor, or at least on the most popular one.
When Tools Lie
Lint's unused resource detection is not perfect. It may miss resources used via reflection or in third-party libraries. Similarly, dependency analysis tools can't always distinguish between compile-time and runtime usage. Always verify by building and testing. A crash on startup is a clear signal that something you removed was needed.
Variations for Different Constraints
Not every team can follow the exact same checklist. Depending on your app's age, architecture, and user base, you may need to adjust the order or depth of trimming. Here are common scenarios and how to adapt.
Legacy App with No Tests
If your app has minimal test coverage, be extra cautious. Remove resources one at a time and run the app manually. Focus on the safest cuts: unused locale folders, duplicate libraries, and native architectures you don't support. Skip aggressive ProGuard changes until you have a safety net. Consider adding a simple smoke test (launch + main flow) before each cleanup session.
Multi-Module Project
In a multi-module setup, bloat can hide in library modules that are shared across features. Run Lint on the entire project, not just the app module. Pay attention to resources in :core or :common modules — they might be used by multiple features, so verify carefully. Use gradle dependencies with the --configuration flag to see what each module pulls in.
App with Heavy Native Code
If your app uses C/C++ libraries (e.g., game engines, signal processing), native libraries may dominate the size. Focus on architecture splits and removing unused .so files. Consider using extractNativeLibs=false in the manifest to prevent extraction, which reduces install-time overhead. Also look for static libraries that are linked but never called — these can sometimes be removed with linker flags.
Team Under Time Pressure
If you only have 15 minutes, skip image optimization and ProGuard tuning. Focus on the top three: unused resources, dependency audit, and native library cleanup. These three steps typically yield the biggest size reduction per minute spent. Set a recurring reminder to do the full checklist every quarter.
App Targeting Low-End Devices
For apps used in emerging markets or on devices with 1 GB RAM or less, every megabyte counts. In addition to the standard checklist, consider: removing HD asset variants, reducing animation frame counts, and stripping debug symbols from native libraries. Also test on a representative low-end device to ensure the app still runs smoothly after trimming.
In all variations, the key is to start with the highest-impact, lowest-risk cuts and work your way down. You don't have to remove everything in one session — incremental progress is better than analysis paralysis.
Pitfalls, Debugging, and What to Check When It Fails
Even with careful planning, bloat removal can break things. Here are the most common failures and how to diagnose them quickly.
App Crashes on Startup
If the app crashes immediately, the most likely cause is a removed resource or library that's needed during initialization. Check the stack trace for ClassNotFoundException or Resources$NotFoundException. If you removed a library, add it back. If you removed a resource, restore it from version control. A quick workaround is to revert the last change and rebuild.
Missing Resources at Runtime
Sometimes the app launches but shows missing icons, broken layouts, or placeholder text. This usually means Lint missed a resource reference. Search for the resource name in the entire project, including XML layouts and Java/Kotlin code. If it's used via reflection or in a library, you may need to keep it. Consider adding a -keep rule for resources that are dynamically referenced.
Build Failures After Dependency Changes
If the build fails with a compile error, a removed dependency was likely required at compile time. Check the error message — it will tell you which class or method is missing. Add the dependency back, or find an alternative. If the build succeeds but runtime fails, the dependency was needed at runtime. Use gradle dependencies to see transitive dependencies that might have been removed indirectly.
Size Reduction Less Than Expected
If you followed the checklist but the size barely changed, you may have missed the biggest contributors. Re-run APK Analyzer and look at the top categories. Often, large assets (videos, fonts, or pre-compressed data) or multiple DEX files are the real culprits. Consider using Android App Bundle to defer some decisions to Google Play. Also check if ProGuard is actually running — sometimes it's disabled in release builds by mistake.
Regression in Performance
Removing resources can sometimes degrade performance if, for example, you removed a precomputed cache or a native library that accelerated a critical path. Profile the app before and after changes. If you see a regression, isolate which removal caused it and consider keeping the resource or finding a lighter alternative.
When all else fails, revert to your baseline and try a more conservative approach. Remove only one category of bloat per cycle (e.g., only unused resources in one release, only dependencies in the next). This makes debugging easier and reduces risk.
Finally, remember that bloat removal is not a one-time task. Set a recurring calendar reminder every two months to run through this checklist. Over time, your team will develop instincts for what to avoid adding in the first place, and the 30-minute session will become a quick sanity check rather than a major cleanup.
Comments (0)
Please sign in to post a comment.
Don't have an account? Create one
No comments yet. Be the first to comment!