Strict Mode: Forcing Android to Delete "Unused" Resources It's Too Afraid to Touch
Beyond shrinkResources: How to use keep.xml and Strict Mode to surgically remove APK bloat that default tools are too conservative to handle.
TL;DR
- Safe Mode (default) is intentionally conservative; it retains assets if it suspects dynamic access (like string concatenation).
- Strict Mode asks the compiler to “prove that this is used,” deleting everything without a static or explicit reference.
keep.xmlis the surgical tool to manage whitelists (tools:keep) and blacklists (tools:discard).- Mental Model: Safe Mode asks, “Could this be used?” Strict Mode asks, “Prove that this is used.”
You’ve enabled minifyEnabled true, turned on shrinkResources true, and spent hours refactoring your R8 rules. You run an APK Analyzer, expecting a surgical reduction in size, only to find that hundreds of "unused" icons, raw JSON files, and localized strings are still clinging to your binary like barnacles on a ship.
The reason? By default, Android’s resource shrinker is intentionally conservative.
In its default “Safe Mode,” it sees a string in your code that might be used to dynamically construct a resource name and it gets defensive. To avoid a Resources.NotFoundException, it retains everything that even remotely looks like a dependency.
If you want a truly precise, high-performance binary, you need to take off the safety gloves. You need Precision Resource Shrinking with keep.xml.
1. The Conflict: Safe Mode vs. Strict Mode
When the resource shrinker analyzes your app, it looks for code-to-resource references. If you use dynamic resource lookups — something like the snippet below — the shrinker enters a defensive state.
// The "Dangerous" lookup
val iconName = "status_" + user.level // Could be status_1, status_2, etc.
val resId = resources.getIdentifier(iconName, "drawable", packageName)In Safe Mode, the shrinker sees the string "status_" and may drastically over-retain resources by marking every resource starting with that prefix as reachable. In complex cases, it might stop shrinking certain folders altogether to avoid a crash.
Strict Mode flips the script. It assumes every resource is unused unless it is:
- Statically referenced in your code (e.g.,
R.drawable.logo_main). - Referenced in the Manifest, Layouts, Styles, or Themes.
- Explicitly whitelisted in a configuration file.
2. Implementing the “Nuke” Option: Strict Mode
To stop the shrinker from guessing, you must explicitly tell it to be aggressive. This is done via a file located at res/raw/keep.xml. (If the folder or file doesn't exist, create it—the name must be exactly keep.xml).
Step 1: Force Strict Shrinking
Inside res/raw/keep.xml, add the following:
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
tools:shrinkMode="strict" />Step 2: The Whitelist (tools:keep)
Once Strict Mode is active, your dynamic lookups will break because the shrinker will delete those “unreferenced” drawables. You must manually whitelist them:
<resources xmlns:tools="http://schemas.android.com/tools"
tools:shrinkMode="strict"
tools:keep="@drawable/status_1,@drawable/status_2,@raw/onboarding_video" />Step 3: The Blacklist (tools:discard)
Sometimes, a library you imported (looking at you, Google Play Services) includes resources you know for a fact you don’t need — like specific localized strings or heavy layouts. You can force-delete them even if they are referenced:
<resources xmlns:tools="http://schemas.android.com/tools"
tools:shrinkMode="strict"
tools:discard="@layout/unused_library_layout,@drawable/heavy_sample_image" />3. Real-World Scenario: Namespacing Your Dynamic Assets
Imagine a Weather App with 50 icons (ic_sun, ic_rain, etc.). You determine the icon at runtime:
fun getWeatherIcon(condition: String): Int {
return context.resources.getIdentifier(
"ic_${condition.lowercase()}",
"drawable",
context.packageName
)
}In Strict Mode, the shrinker deletes all 50 icons because it sees no static R.drawable.ic_sun calls.
The Solution: Use wildcards with aggressive namespacing.
<resources xmlns:tools="http://schemas.android.com/tools"
tools:shrinkMode="strict"
tools:keep="@drawable/ic_weather_*" />Pro-Tip: Don’t use generic prefixes like
ic_*. In large projects, this accidentally whitelists legacy assets or library icons. Namespace your dynamic assets (e.g.,ic_weather_) to keep your whitelist surgical.
4. When Strict Mode Is a Bad Idea
Strict Mode trades safety for precision. Avoid it if your app:
- Relies on Reflection: Uses libraries that access resources via reflection without documented usage.
- Dynamic Modules: Loads third-party feature modules dynamically with poorly defined asset boundaries.
- Limited QA: Lacks the release QA coverage needed to catch
Resources.NotFoundExceptionbefore production.
5. Forensic Auditing: resources.txt
Verification is the cornerstone of senior-level shrinking. When you build a release APK or AAB, Android Studio generates a report at: app/build/outputs/mapping/release/resources.txt
This file is your source of truth. It logs every resource kept, every resource discarded, and — critically — why. If a resource you thought was unused is still there, resources.txt will show you the "reachable" path that saved it.
6. Safe Rollout Checklist
- Enable Strict Mode only in a release build type or a specific staging build.
- Audit
resources.txtbefore and after the change to quantify savings. - Add namespaced
tools:keeprules for all resources fetched viagetIdentifier(). - Smoke-test every screen that uses dynamic resource lookups.
- Ship to an internal test track or use a phased rollout (5%) to ensure no runtime crashes.
💬 Audience Engagement
- The Challenge: Enable
Strict Modetoday. How much weight did you lose? I’ve seen apps drop 2–5MB just by stopping the shrinker from "protecting" unused library drawables. - The Dynamic Risk: Are your
keeprules namespaced well enough to survive a legacy asset cleanup? - Discussion: What’s the weirdest “ghost resource” you’ve found in your APK during an audit?
📘 Master Your Next Technical Interview
Since Java is the foundation of Android development, mastering DSA is essential. I highly recommend “Mastering Data Structures & Algorithms in Java”. It’s a focused roadmap covering 100+ coding challenges to help you ace your technical rounds.
- E-book (Best Value! 🚀): $1.99 on Google Play
- Kindle Edition: $3.49 on Amazon
- Also available in Paperback & Hardcover.

Comments
Post a Comment