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.

Strict Mode: Forcing Android to Delete "Unused" Resources It's Too Afraid to Touch

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.xml is 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.

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:

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:

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:

3. Real-World Scenario: Namespacing Your Dynamic Assets

Imagine a Weather App with 50 icons (ic_sunic_rain, etc.). You determine the icon at runtime:

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.

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.NotFoundException before 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

  1. Enable Strict Mode only in a release build type or a specific staging build.
  2. Audit resources.txt before and after the change to quantify savings.
  3. Add namespaced tools:keep rules for all resources fetched via getIdentifier().
  4. Smoke-test every screen that uses dynamic resource lookups.
  5. Ship to an internal test track or use a phased rollout (5%) to ensure no runtime crashes.

💬 Audience Engagement

  • The Challenge: Enable Strict Mode today. 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 keep rules 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.

Comments

Popular posts from this blog

No More _state + state: Simplifying ViewModels with Kotlin 2.3

Why You Should Stop Passing ViewModels Around Your Compose UI Tree 🚫

Is Jetpack Compose Making Your APK Fatter? (And How to Fix It)