Personnaliser les ressources à conserver

Lorsque vous activez l'optimisation de l'application, le comportement par défaut de l'optimiseur est différent selon les versions de R8.

  • Avec le DSL mis à jour, disponible à partir de AGP 9.3 et versions ultérieures, la réduction des ressources est activée par défaut lorsque l'optimisation est activée. Notez que l'ancien DSL, qui nécessite l'activation distincte de l'optimisation du code et des ressources, est toujours compatible.
  • Pour les versions antérieures à AGP 9.3, le paramètre isShrinkResources = truedemande à l'optimiseur de supprimer les ressources inutilisées, ce qui permet de réduire la taille de votre application. La réduction des ressources ne fonctionne qu'en association avec la réduction du code. Par conséquent, si vous optimisez les ressources, définissez également isMinifyEnabled = true.

AGP 9.3 et versions ultérieures (Kotlin)

buildTypes {
    release {
        optimization {
            enable = true // Enables code and resource optimizations.
        }
    }
}

AGP 9.3 et versions ultérieures (Groovy)

buildTypes {
    release {
        optimization {
            enable = true // Enables code and resource optimizations.
        }
    }
}

Ancien DSL (Kotlin)

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

Ancien DSL (Groovy)

buildTypes {
    release {
        minifyEnabled = true
        shrinkResources = true
        ...
    }
}

Si vous souhaitez conserver ou supprimer des ressources spécifiques, créez un fichier XML keep dans les ressources de votre projet, par exemple res/raw/my.package.keep.xml. Le fichier keep comporte les composants suivants :

  • Balise <resources> : contient tous les éléments de ressources enfants et les attributs keep/discard.
  • Attribut tools:keep : accepte une liste de noms de ressources séparés par une virgule qui identifient les ressources à conserver.
  • Attribut tools:discard : accepte une liste de noms de ressources séparés par une virgule qui identifient les ressources à supprimer.

Utilisez l'astérisque comme caractère générique pour référencer plusieurs ressources dans le même dossier, par exemple :

<?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" />

Spécifier les ressources à supprimer peut sembler superflu, alors que vous pourriez les supprimer, mais cela peut s'avérer utile lorsque vous utilisez des variantes de compilation.

Cibler des variantes de compilation spécifiques

Pour supprimer des ressources dans certaines variantes de compilation uniquement, placez toutes vos ressources dans le répertoire de projet commun, puis créez un fichier my.package.build.variant.keep.xml différent pour chaque variante de compilation dans le répertoire de ressources de la variante. Dans le fichier keep, spécifiez manuellement les ressources à supprimer lorsqu'une ressource donnée semble être utilisée dans le code (et n'est donc pas supprimée par le réducteur), mais que vous savez qu'elle ne sera pas utilisée pour la variante de compilation indiquée.

Supprimer les autres ressources non utilisées

L'optimiseur ne supprime que les ressources qui ne sont pas référencées par le code de votre application. Cela signifie qu'il ne supprime pas les autres ressources correspondant à des configurations d'appareil différentes.

Utilisez la propriété Android Gradle resConfigs dans le fichier build.gradle du module de votre application pour supprimer les fichiers d'autres ressources dont votre application n'a pas besoin.

Par exemple, si vous utilisez une bibliothèque qui contient des ressources linguistiques (comme les services Google Play), votre application inclut toutes les chaînes traduites pour les messages de ces bibliothèques, et ce, que le reste de votre application soit traduite ou non dans les langues de ces ressources. Pour ne conserver que les langues officiellement compatibles avec votre application, spécifiez-les à l'aide de la propriété resConfigs. Toutes les ressources pour les langues non spécifiées sont supprimées.

Les extraits suivants montrent comment limiter vos ressources linguistiques à l'anglais et au français :

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

ou

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

Lorsque vous publiez une application au format Android App Bundle (AAB), seules les langues configurées sur l'appareil de l'utilisateur sont téléchargées par défaut lors de l'installation de l'application. De même, seules les ressources correspondant à la densité d'écran de l'appareil et les bibliothèques natives correspondant à l'ABI de l'appareil sont incluses dans le téléchargement. Pour en savoir plus, consultez Réactiver ou désactiver des types de fichiers APK de configuration.

Pour les anciennes applications publiées avec des APK (créés avant août 2021), vous pouvez personnaliser la densité d'écran ou les ressources ABI à inclure dans votre APK en compilant plusieurs APK ciblant chacun une configuration d'appareil différente.

Éviter les conflits lors de la fusion de ressources

Par défaut, le plug-in Android Gradle (AGP) fusionne les ressources portant un nom identique, telles que les drawables du même nom qui se trouvent dans des dossiers de ressources différents. Ce comportement n'est pas contrôlé par la propriété shrinkResources et ne peut pas être désactivé, car il est nécessaire pour éviter les erreurs lorsque plusieurs ressources portent le nom auquel votre code fait référence.

La fusion de ressources ne se produit que lorsque plusieurs fichiers partagent les mêmes nom de ressource, type et qualificatif. AGP sélectionne le fichier qu'il considère comme le meilleur candidat parmi les doublons (en fonction d'un ordre de priorité décrit ci-dessous) et transmet uniquement cette ressource à AAPT en vue d'une distribution dans l'artefact de compilation final.

AGP recherche les ressources en double aux emplacements suivants :

  • Ressources principales, associées à l'ensemble de sources principal, généralement situées dans src/main/res/
  • Superpositions de variantes, à partir du type de compilation et des versions de compilation
  • Dépendances du projet de bibliothèque

AGP fusionne les ressources en double dans l'ordre de priorité en cascade suivant :

Dépendances → Principales → Version de compilation → Type de compilation

Par exemple, si une ressource en double apparaît à la fois dans vos ressources principales et dans une version de compilation, Gradle sélectionne celle qui se trouve dans la version de compilation.

Si des ressources identiques apparaissent dans le même ensemble de sources, Gradle ne peut pas les fusionner et génère une erreur de fusion des ressources. Cela peut se produire si vous définissez plusieurs ensembles de sources dans la propriété sourceSet du fichier build.gradle de votre module ; par exemple si src/main/res/ et src/main/res2/ contiennent des ressources identiques.

Résoudre les problèmes de réduction de ressources

Lorsque vous réduisez des ressources, la fenêtre Compiler affiche un récapitulatif des ressources supprimées de l'application. (Cliquez sur Changer d'affichage sur le côté gauche de la fenêtre pour afficher la sortie texte détaillée à partir de Gradle.) Exemple :

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

Gradle crée également un fichier de diagnostic nommé resources.txt dans <module-name>/build/outputs/mapping/release/ (il s'agit du même dossier que celui des fichiers de sortie de ProGuard output files). Le fichier inclut des informations telles que les ressources qui en référencent d'autres, ainsi que les ressources utilisées ou supprimées.

Par exemple, pour savoir pourquoi @drawable/ic_plus_anim_016 se trouve toujours dans votre application, ouvrez le fichier resources.txt et recherchez ce nom de fichier. Vous constaterez peut-être qu'il est référencé à partir d'une autre ressource :

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

Vous devez maintenant comprendre pourquoi @drawable/add_schedule_fab_icon_anim est accessible. Si vous effectuez une recherche vers le haut, vous verrez que cette ressource est répertoriée sous l'en-tête The root reachable resources are: (Les ressources accessibles à la racine sont les suivantes :) dans resources.txt.

Cela signifie qu'il existe une référence à add_schedule_fab_icon_anim (en d'autres termes, son ID R.drawable a été trouvé dans le code accessible).

Si vous n'utilisez pas de vérification stricte, un ID de ressource peut être marqué comme accessible s'il existe des constantes de chaîne susceptibles d'être utilisées pour créer des noms pour des ressources chargées dynamiquement. Dans ce cas, si vous recherchez le nom de la ressource dans le résultat de la compilation, un message semblable à celui-ci peut s'afficher :

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.

Si vous voyez l'une de ces chaînes et que vous êtes sûr qu'elle n'est pas utilisée pour charger dynamiquement la ressource donnée, utilisez l'attribut tools:discard dans votre fichier keep pour demander au système de compilation de supprimer la ressource.