Customize which resources to keep

When you enable app optimization, the isShrinkResources = true setting instructs the optimizer to remove resources that are unused, which helps reduce the size of your app. Resource shrinking works only in conjunction with code shrinking, so if you're optimizing resources, also set isMinifyEnabled = true, for example:

buildTypes {
    release {
        isMinifyEnabled = true
        isShrinkResources = true
        ...
    }
}

If you want to keep or discard specific resources, create an XML keep file in your project resources, for example res/raw/my.package.keep.xml. The keep file has the following components:

  • <resources> tag — Contains all child resource elements and keep/discard attributes.
  • tools:keep attribute — Accepts a comma-separated list of resource names that identify resources to keep
  • tools:discard attribute — Accepts a comma-separated list of resource names that identify resources to discard

Use the asterisk character as a wildcard to reference multiple resources in the same folder, for example:

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
    tools:keep="@layout/l_used*_c,@layout/l_used_a,@layout/l_used_b*"
    tools:discard="@layout/unused2" />

Specifying which resources to discard might seem superfluous when you could instead delete them, but discarding resources can be useful when using build variants.

Target specific build variants

To remove resources in only some build variants, put all your resources into the common project directory, then create a different my.package.build.variant.keep.xml file for each build variant in the variant's resource directory. In the keep file, manually specify resources to remove when a given resource appears to be used in code (and therefore not removed by the shrinker), but you know it actually won't be used for the given build variant.

Remove unused alternative resources

The optimizer only removes resources that aren't referenced by your app code, which means the optimizer won't remove alternative resources for different device configurations.

Use the Android Gradle resConfigs property in your app's module build.gradle file to remove alternative resource files that your app does not need.

For example, if you are using a library that includes language resources (such as Google Play Services), then your app includes all translated language strings for the messages in those libraries whether the rest of your app is translated to the same languages or not. To keep only the languages that your app officially supports, specify those languages using the resConfigs property. Any resources for languages not specified are removed.

The following snippets show how to limit your language resources to just English and French:

android {
    defaultConfig {
        ...
        resourceConfigurations.addAll(listOf("en", "fr"))
    }
}

or

android {
    defaultConfig {
        ...
        resConfigs "en", "fr"
    }
}

When you publish an app using the Android App Bundle (AAB) format, by default only languages configured on a user's device are downloaded when the user installs the app. Similarly, only resources matching the device's screen density and native libraries matching the device's ABI are included in the download. For more information, see Re-enable or disable types of configuration APKs.

For legacy apps releasing with APKs (created before August 2021), you can customize the screen density or ABI resources to include in your APK by building multiple APKs that target different device configurations.

Avoid conflicts when merging resources

By default, the Android Gradle plugin (AGP) merges identically named resources, such as drawables with the same name that are in different resource folders. This behavior is not controlled by the shrinkResources property and cannot be disabled because the behavior is necessary to avoid errors when multiple resources have the name your code is referencing.

Resource merging occurs only when two or more files share an identical resource name, type, and qualifier. AGP selects the file it identifies to be the best choice among the duplicates (based on a priority order described below) and passes only that one resource to AAPT for distribution in the final build artifact.

AGP looks for duplicate resources in the following locations:

  • Main resources, associated with the main source set, generally located in src/main/res/
  • Variant overlays, from the build type and build flavors
  • Library project dependencies

AGP merges duplicate resources in the following cascading priority order:

Dependencies → Main → Build flavor → Build type

For example, if a duplicate resource appears in both your main resources and a build flavor, Gradle selects the resource in the build flavor.

If identical resources appear in the same source set, Gradle cannot merge them and emits a resource merge error. This can happen if you define multiple source sets in the sourceSet property of your module build.gradle file, for example, if both src/main/res/ and src/main/res2/ contain identical resources.

Troubleshoot resource shrinking

When you shrink resources, the Build window shows a summary of the resources removed from the app. (Click Toggle view on the left side of the window to display detailed text output from Gradle.) For example:

:android:shrinkDebugResources
Removed unused resources: Resource data reduced from 2570KB to 1711KB: Removed 33%
:android:validateDebugSigning

Gradle also creates a diagnostic file named resources.txt in <module-name>/build/outputs/mapping/release/ (the same folder as ProGuard's output files). The file includes details such as which resources reference other resources and which resources are used or removed.

For example, to find out why @drawable/ic_plus_anim_016 is still in your app, open the resources.txt file and search for that filename. You might find that it's referenced from another resource:

16:25:48.005 [QUIET] [system.out] @drawable/add_schedule_fab_icon_anim : reachable=true
16:25:48.009 [QUIET] [system.out] @drawable/ic_plus_anim_016

You now need to know why @drawable/add_schedule_fab_icon_anim is reachable; and if you search upwards, you'll find the resource listed under the heading The root reachable resources are: in resources.txt.

This means there's a code reference to add_schedule_fab_icon_anim, that is, its R.drawable ID was found in the reachable code.

Unless you're using strict checking, resource IDs can be marked as reachable if there are string constants that look like they might be used to construct resource names for dynamically loaded resources. In that case, if you search the build output for the resource name, you might find a message like this:

10:32:50.590 [QUIET] [system.out] Marking drawable:ic_plus_anim_016:2130837506
    used because its format-string matches string pool constant ic_plus_anim_%1$d.

If you see one of these strings and you are certain that the string is not being used to load the given resource dynamically, use the tools:discard attribute in your keep file to inform the build system to remove the resource.