Gradle & Code Quality

shrinkResources: Remove Unused Assets Without Breaking Dynamic Lookups

shrinkResources removes resources that the Android optimizer determines are unused. It is most effective alongside code optimization, but resources selected dynamically need special attention because the build may not see the runtime relationship.

By , FounderUpdated 3 min read

Enable it with code optimization

For a Kotlin DSL release block:

KOTLIN · REFERENCE EXAMPLE
release {
    isMinifyEnabled = true
    isShrinkResources = true
    proguardFiles(
        getDefaultProguardFile("proguard-android-optimize.txt"),
        "proguard-rules.pro"
    )
}

This fragment belongs inside the module's buildTypes. Check the current optimizer guidance, because resource-shrinking integration has evolved across AGP releases.

Distinguish resources from arbitrary assets

Android resources, raw files in assets, native libraries and DEX code have different packaging and retention rules. A resource-shrinking setting does not promise to compress every large file in an APK.

Use the app-size guide to identify the dominant category before choosing an optimization. Removing an unused locale or oversized image may matter more than changing a code rule.

Audit dynamic resource names

Code that builds resource names at runtime, remote configuration selecting a drawable, and reflection-like lookup can hide usage from static analysis. Prefer explicit resource references where practical.

Where a dynamic contract is necessary, retain only the required names using the resource-shrinker mechanisms documented for your build version. Do not keep every resource simply because one remotely selected icon disappeared.

Compare equivalent builds

Build before and after from the same source revision, dependency set and device targeting. Compare both the archive and representative installed behavior. Bundle-generated device APKs and a universal APK are not directly equivalent size measurements.

Test language changes, dark mode, density-specific images and less common screens. The homepage working does not establish that all resource configurations survived.

Keep a recovery trail

Record which resource disappeared, how it is selected and why the retention rule is necessary. This makes future cleanup possible without repeating the same regression.

APKLint's size analysis can help locate large packaged entries, and its optimization checker can support review. Neither executes every runtime resource lookup, so use those findings to focus device testing rather than declare the release safe automatically.

Review a remotely selected promotion icon

Imagine a promotion screen that receives the string summer_badge from a server and resolves a drawable by name. The source contains no ordinary reference to that resource, so code review must account for the dynamic name contract. Prefer a finite, explicit mapping from allowed server values to resource IDs when practical. Unknown values should select a deliberate fallback instead of crashing or revealing arbitrary resources.

If the existing design genuinely requires dynamic lookup, use the resource-retention mechanism supported by the project's build version and retain only the relevant resources. Keep the server's allowed-value contract with the app change. Removing a badge from the app while the server can still request it is a compatibility change even when a local default screen looks correct.

Account for changes in the optimization pipeline

The optimizer documentation, reviewed on 29 September 2026, describes version-dependent resource optimization and the newer unified DSL. The earlier legacy flags remain useful for projects using that configuration. Do not combine settings from different plugin generations without checking which ones the pinned version recognizes and enables.

Compare the actual packaged resource inventory before and after the change. Use equivalent release artifacts: the same source, dependencies, locale selection, density and ABI targeting. A universal package and a device-specific package can differ substantially even when resource shrinking did nothing.

For the promotion example, exercise every server-selectable badge, an unknown value, dark mode and a nondefault locale. Check a less common density as well as the main test phone. Record the observed fallback for a resource that is intentionally unavailable. Then inspect whether unrelated images, raw assets and native libraries remain; their presence may be correct and should not be judged solely by the shrink flag. Preserve the rule's reason so later cleanup can remove it when the dynamic lookup is retired. This focuses package-size work on measurable entries and runtime contracts rather than retaining the entire resource tree after the first missing-icon report.

Sources and further reading

  1. Android Developers: Enable optimization with R8
  2. Android Developers: Add keep rules
  3. Android Developers: Reduce your app size

Reference review: 29 September 2026. Examples illustrate the workflow; check your installed versions, release artifact and account-specific Console requirements before applying them. This guide is not a claim that APKLint executed your project or verified your private account.

APKLint

Android inspection tools and practical release guides. About APKLint · Report a correction