Le système de compilation de la plate-forme Android (Soong) exécute le compilateur R8 sur les cibles qui compilent le bytecode DEX, y compris les applications Android (android_app), les tests (android_test) et les bibliothèques Java installables avec compile_dex: true (comme services.jar), pour réduire, optimiser et supprimer le code et les ressources inutilisés. Pour les bibliothèques statiques (android_library et java_library statique), Soong n'exécute pas R8 directement, mais utilise le bloc de propriétés optimize pour associer et propager les règles de conservation des consommateurs (export_proguard_flags_files: true) aux cibles DEX en aval qui les associent de manière statique.
Pour les ingénieurs de plate-forme qui créent des images système ou des packages de fournisseurs, l'élimination des règles de conservation trop larges offre des avantages directs pour l'intégrité du système :
- Empreinte plus petite de la partition système : R8 supprime les classes, méthodes et ressources inutilisées avant d'empaqueter les APK et les JAR sur
/system,/system_ext,/productet/vendor. - Artefacts compilés plus petits : moins de méthodes DEX signifie des fichiers
.odexet.vdexplus petits générés pardex2oatlors de la compilation au moment de la compilation ou sur l'appareil. - Pression de mémoire d'exécution réduite : comme Android mappe les tables de ressources
.odex,.vdexet APK dans la mémoire du processus à l'aide d'appelsmmapà la pagination à la demande, les binaires plus petits réduisent les défauts de page majeurs lors du démarrage de l'application et diminuent la mémoire de code et de ressources résidente dans les processus. Pour en savoir plus sur l'impact du code d'application compilé sur la mémoire de l'appareil, consultez Le code d'application est de la mémoire. - Optimisation plus efficace de l'ensemble du programme : les contraintes de conservation étroites permettent à R8 d'intégrer des méthodes, de dévirtualiser les appels d'interface et virtuels, d'élaguer les champs inutilisés et de propager les constantes dans les classes.
Configurer l'optimisation R8 dans Soong
Dans les fichiers Android.bp, configurez l'optimisation R8 à l'aide du bloc de propriétés optimize sur les cibles android_app et java_library installables (ou sur les modules android_library et java_library statiques pour exporter les règles de conservation des consommateurs) :
android_app {
name: "MySystemApp",
srcs: ["src/**/*.java"],
optimize: {
obfuscate: true,
shrink_resources: true,
},
}
Le tableau suivant récapitule les propriétés optimize les plus courantes dans Soong (définies dans build/soong/java/dex.go) :
| Propriété | Description |
|---|---|
enabled |
Détermine si R8 s'exécute sur la cible. La valeur par défaut est true pour toutes les cibles android_app.
|
shrink |
Contrôle le tree shaking pour supprimer les classes, les champs et les méthodes inaccessibles. La valeur par défaut est true pour les cibles android_app (false pour les modules de test et java_library autonomes, qui transmettent -dontshrink sauf si elle est explicitement définie).
|
optimize |
Contrôle les optimisations du bytecode, telles que l'intégration de méthodes, la fusion de classes, la propagation de constantes et la suppression des branches mortes. La valeur par défaut est true pour les cibles android_app (contrôlées par l'indicateur de compilation de version RELEASE_R8_OPTIMIZE_BY_DEFAULT).
|
obfuscate |
Contrôle la minification et le renommage des identifiants. La valeur par défaut est false pour la compatibilité historique, mais définissez-la sur true chaque fois que possible pour réduire la taille DEX et débloquer des optimisations plus poussées (voir Activer l'obscurcissement chaque fois que possible).
|
shrink_resources |
Supprime les ressources inutilisées (res/ entrées) de l'APK empaqueté après la minification de code. La valeur par défaut est false.
|
optimized_shrink_resources |
Exécute le pipeline intégré de minification du code et des ressources R8 afin que R8 trace conjointement les références de code et de ressources en une seule passe. Lorsque shrink_resources: true est défini, la valeur par défaut est RELEASE_USE_OPTIMIZED_RESOURCE_SHRINKING_BY_DEFAULT (true dans les versions standard de la plate-forme).
|
proguard_flags_files |
Liste les fichiers .flags ou .pro spécifiques au module contenant des règles de conservation personnalisées. Évitez d'ajouter des fichiers personnalisés lorsque des annotations ou des valeurs par défaut standards suffisent.
|
export_proguard_flags_files |
Propage le proguard_flags_files de cette bibliothèque aux modules en aval qui en dépendent de manière statique.
|
trace_references_from |
Liste les cibles associées de la bibliothèque Java dont les références de bytecode dans cette cible sont automatiquement suivies et conservées par R8. |
Activer l'obscurcissement chaque fois que possible
Dans Soong, obfuscate est défini par défaut sur false pour la compatibilité historique, mais vous devez définir explicitement obfuscate: true sur android_app et les cibles DEX autonomes dans la mesure du possible :
- Empreinte DEX et
.vdexplus petite : renommer les packages, les classes, les champs et les méthodes en identifiants courts (a,b) réduit le pool de chaînes DEX (string_idsetstring_data_item) et les descripteurs de type. Étant donné qu'Android mappe les fichiers.vdexet.odexdans la mémoire du processus, des tables de symboles plus petites réduisent directement la taille de la partition système et l'espace mémoire utilisé d'exécution. - Optimisations plus approfondies de l'ensemble du programme : autoriser R8 à renommer les identifiants permet de fusionner les classes, d'aplatir les packages et de dédupliquer les classes synthétiques dans les packages que R8 doit normalement ignorer pour éviter les conflits de noms.
- Symbolisation complète de la trace de pile : l'activation de l'obscurcissement dans Soong ne réduit pas la capacité de débogage. Pour chaque cible compilée par R8, Soong émet un fichier de mappage
proguard_dictionarydans le répertoire intermédiaire du module, regroupe tous les dictionnaires de modules dans l'artefactproguard-dict.zipde compilation, intègre un hachage de mappage (--map-id-template) dans l'en-tête DEX et réécrit les attributs de classeSourceFile(--source-file-template) afin queretracepuisse symboliser automatiquement les traces de pile.
Ne conservez obfuscate: false (ou conservez explicitement les noms d'API publiques) que pour :
- Bibliothèques partagées sur le bootclasspath ou le classpath
system_server: les modules (java_libraryoujava_sdk_library) qui exposent une API externe et qui sont liés dynamiquement par d'autres modules au moment de l'exécution (tels que les ciblesframework.jar,services.jarou<uses-library>) doivent conserverobfuscate: falseou préserver explicitement leur surface d'API à l'aide des règles@KeepForApiou-keep(ainsi queprotect_api_surface: truepour les cibles bootclasspath). Les appelants compilés par rapport à leurs stubs peuvent ainsi résoudre les noms de classes et de membres au moment de l'exécution.
Avant d'activer obfuscate: true sur une application, vérifiez les conditions préalables à la migration suivantes :
- Dépendances externes de l'APK de test : si un APK
android_testexterne définitinstrumentation_for: "MyApp"et appelle directement des classes ou des méthodes internes deMyApp, le fait de renommer ces symboles entraîneNoSuchMethodErrorouNoClassDefFoundErrorlors de l'exécution du test. Il est préférable de structurer le test en tant qu'APK auto-instrumenté liantMyApp.implde manière statique, d'annoter les hooks de test avec@VisibleForTestingou de configurertrace_references_from(voir Étape 2 : Migrer les règles Keep axées sur les tests) afin que les symboles internes nécessaires aux tests soient conservés tout en obscurcissant le reste de l'application. - Réflexion basée sur des chaînes et JNI : si un module recherche des classes, des méthodes ou des champs par des noms de chaînes littérales (
Class.forName,getDeclaredMethodou JNIFindClassetGetMethodID) sans règles ni annotations de conservation, l'obscurcissement renomme ces cibles et interrompt les recherches d'exécution. (Notez que la réflexion non annotée échoue également sousshrink: trueetoptimize: true.) Annotez ces points d'entrée avec le guide sur les annotations à conserver (@UsesReflection,@UsedByReflectionou@UsedByNative) afin que R8 conserve leurs noms et obscurcisse le reste du module.
Suivez le principe de l'absence d'indicateurs personnalisés
Le module de plate-forme Android idéal ne comporte aucun fichier proguard.flags ni keep.xml personnalisé. Dans la compilation de la plate-forme Android, la grande majorité des règles de conservation personnalisées sont redondantes :
- Lignes de base de la plate-forme et AAPT2 : la plupart des points d'entrée sont déjà conservés automatiquement par les lignes de base de la plate-forme globale (comme
@Keep, les méthodes JNInativeet@VisibleForTesting) ou générés par AAPT2 à partir deAndroidManifest.xmlet des ressources de mise en page (voir Comprendre les règles de conservation par défaut de la plate-forme). - Alternatives ciblées : lorsque les points d'entrée doivent être conservés, préférez les annotations de site de déclaration (
keepanno,@VisibleForTesting), les règles de bibliothèque exportées (export_proguard_flags_files: true) ou le linking de test statique aux fichiers.flagsdétachés (consultez Auditer et migrer les règles Keep existantes).
Avant d'ajouter ou de conserver un fichier proguard.flags ou keep.xml personnalisé, vérifiez si la règle est déjà gérée par les références de base par défaut ou si elle peut être migrée vers des annotations de code.
Comprendre les règles de conservation par défaut de la plate-forme
Soong transmet automatiquement les règles de base globales suivantes à chaque invocation R8 sur les cibles de compilation DEX (android_app, android_test et modules java_library installables), configurées dans build/soong/java/dex.go :
build/make/core/proguard.flags: conserve les classes et les membres annotés avec@com.android.internal.annotations.VisibleForTestingdans tous les packages, et avec@VisibleForTesting(androidx.annotation.VisibleForTestingoucom.google.common.annotations.VisibleForTesting) dans les packagesandroid.**,com.android.**etcom.google.android.**. Préserve également@TestApi,@Keep(androidx.annotation,android.support.annotation,com.android.internal.annotations),@KeepForWeakReference,@WeaklyReferencedCallbacket@dalvik.annotation.optimization.**.build/make/core/proguard_basic_keeps.flags: conserve les attributsSourceFilepour les traces de pile, les annotations de visibilité d'exécution (attributsRuntimeVisible*Annotations), les attributsExceptionsetAnnotationDefault, les méthodesnative, les membresSerializable, les méthodes@JavascriptInterface, les constructeursThrowable(String), les champsParcelable$CREATORet les champsMessageLiteprotobuf.build/make/core/proguard/kotlin.flags: désactive les avertissements inoffensifs pour des méta-annotations Kotlin spécifiques (kotlin.Metadataetkotlin.annotation.{AnnotationRetention,AnnotationTarget,Retention,Target}) et supprime les annotationsDebugMetadatade Kotlin dans les versions Release.build/make/core/proguard/checknotnull.flags: remplace les appels d'assistance courants pour la vérification des valeurs null (com.google.common.base.Preconditions.checkNotNulletdagger.internal.Preconditions.checkNotNull*) par des vérifications concises des valeurs null du bytecode. Ce fichier omet intentionnellementObjects.requireNonNullpour préserver les messages d'exception explicites au-delà des limites de l'API du framework.build/make/core/proguard/enumvalues.flags: conserve les méthodesvaluesetvalueOfsur les typesenum, sauf si elles sont désactivées par la configuration de compilation.- Règles générées automatiquement par AAPT2 : AAPT2 inspecte les fichiers XML
AndroidManifest.xml, de mise en page et de préférences fusionnés pour générer des règles keep exactes pour chaque méthodeActivity,Service,BroadcastReceiver,ContentProvider,BackupAgent,Application, sous-classeViewpersonnalisée, sous-classePreferenceet XMLandroid:onClickenregistrée.
Auditer et migrer les règles de conservation existantes
Lorsque vous auditez des fichiers proguard.flags ou keep.xml existants dans un dépôt de plate-forme, évaluez chaque règle dans l'ordre par rapport à la hiérarchie en quatre étapes suivante :
Étape 1 : Supprimez les règles redondantes ou obsolètes
Supprimez les règles déjà couvertes par les règles de base globales, les règles de mise en page et de fichier manifeste AAPT2, ou les paramètres par défaut R8, ainsi que les règles faisant référence à des classes ou des packages qui n'existent plus.
Si toutes les règles d'un fichier .flags sont redondantes, supprimez le fichier et retirez proguard_flags_files de Android.bp. Si le bloc optimize restant ne fait que réaffirmer les paramètres par défaut, supprimez-le et mettez en forme le fichier build avec bpfmt -w Android.bp. Lorsque vous nettoyez un package, vérifiez si les modules associés dans les sous-répertoires (tels que les variantes cibles Kotlin) font référence au même fichier d'indicateurs et mettez-les à jour ensemble.
Étape 2 : Migrer les règles de conservation axées sur les tests
Déplacez les règles de conservation axées sur les tests hors des fichiers .flags de production personnalisés à l'aide de l'un des modèles suivants :
Associez la bibliothèque d'implémentation aux tests (
static_libs) : lorsqu'unandroid_testexerce des détails d'implémentation internes ou privés à un package d'une application, l'architecture de plate-forme recommandée consiste à placer les fichiers sources de l'application dans unandroid_library(MyApp.impl) et à associer statiquementMyApp.implàMyAppetMyAppTests:android_library { name: "MyApp.impl", srcs: ["src/**/*.java"], manifest: "AndroidManifest.xml", } android_app { name: "MyApp", static_libs: ["MyApp.impl"], optimize: { obfuscate: true, shrink_resources: true, }, } android_test { name: "MyAppTests", srcs: ["tests/**/*.java"], static_libs: ["MyApp.impl"], }Ce modèle à trois modules permet à
MyAppTestsde s'exécuter en tant que test d'auto-instrumentation avec un accès complet aux classes internes, permet àMyAppd'activerobfuscate: truelibrement sans interrompre les tests, évite d'expédier des points d'entrée réservés aux tests dans l'APK de production et élimine la reconstruction deMyApplorsque le code de test change.Annoter les hooks de test avec
@VisibleForTesting: si un APK de test externe appelle un petit nombre de méthodes ou de constructeurs internes sur une cibleandroid_appet que la restructuration en bibliothèque.impln'est pas pratique, annotez ces déclarations avec@VisibleForTesting. La référence globalebuild/make/core/proguard.flagsconserve automatiquement les éléments@VisibleForTestingdans les packagesandroid.**,com.android.**etcom.google.android.**sans fichiers.flagspersonnalisés. (Pour les modules de fournisseur en dehors de ces espaces de noms, utilisez@UsedByReflectionou les règles de bibliothèque exportées.)Utilisez
trace_references_fromau-delà des limites de la bibliothèque multiplate-forme : lorsque les tests ne peuvent pas lier statiquement l'implémentation cible (par exemple, les tests exerçant des services de serveur système ou des fichiers JAR de plate-forme tels queframework-connectivity), configureztrace_references_fromsur le module cible en pointant vers un module complémentaire de bibliothèque Java contenant les sources de test. R8 suit et conserve toutes les classes et tous les membres référencés par le bytecode associé.
Étape 3 : Exportez les règles de la bibliothèque propriétaire
Si un java_library ou un android_library partagé nécessite des règles de conservation pour ses propres rappels de réflexion interne ou JNI, définissez les règles sur la cible de la bibliothèque et définissez export_proguard_flags_files: true :
java_library {
name: "my-shared-library",
srcs: ["src/**/*.java"],
optimize: {
proguard_flags_files: ["proguard.flags"],
export_proguard_flags_files: true,
},
}
Toutes les cibles android_app et de bibliothèque en aval qui associent statiquement my-shared-library héritent automatiquement de ces règles. Les applications en aval n'ont donc pas besoin de les dupliquer. Lorsque plusieurs modules de différents répertoires doivent partager un ensemble de règles qui n'est pas lié à une seule bibliothèque de code, publiez le fichier .flags via un filegroup explicite dans Android.bp afin que les modules puissent référencer :my-shared-flags de manière propre au-delà des limites des packages. Pour obtenir des conseils généraux sur la création de règles de conservation des consommateurs de bibliothèque sans restreindre l'optimisation des applications en aval, consultez Optimisation pour les auteurs de bibliothèques.
Étape 4 : Annoter les déclarations dans le code source
Lorsqu'une classe, une méthode, un constructeur ou un champ sont accessibles par réflexion ou JNI et ne sont pas couverts par AAPT2 ni par les références globales, remplacez les règles -keep détachées dans les fichiers .flags par des annotations de source issues du Guide to Keep Annotations (consultez la documentation Javadoc keepanno) :
@UsesReflection: préférez cette annotation lorsque vous contrôlez le code effectuant la réflexion. Placez-le au niveau de l'appel de réflexion pour déclarer les classes, méthodes ou champs cibles auxquels vous accédez de manière dynamique. Étant donné que@UsesReflectionencode automatiquement une précondition selon laquelle le site d'appel annoté lui-même est accessible, R8 supprime à la fois l'appelant et la cible de réflexion si le site d'appel n'est pas utilisé.@UsedByReflection: à placer sur les classes, les méthodes, les champs ou les constructeurs qui sont instanciés ou appelés de manière réflexive par du code ou des bibliothèques externes, comme les classes chargées avecClass.forNameà partir des clésBundleouSettings, ou les classes de plug-in chargées dans des chargeurs de classe dynamiques. Spécifiezkind(par exemple,KeepItemKind.CLASS_AND_METHODS),preconditions(@KeepCondition) et les contraintes de paramètres afin que R8 ne conserve que le contrat de réflexion exact et puisse toujours optimiser ou supprimer les membres inutilisés de la classe. N'ajoutez pas@UsedByReflectionaux composants enregistrés dans le fichier manifeste, tels queJobServiceouBroadcastReceiver, car AAPT2 les conserve déjà.@UsedByNative: à placer sur les méthodes ou les champs auxquels le code JNI C ou C++ accède à l'aide deGetMethodID,GetStaticMethodIDouGetFieldID.@KeepForApi: à placer sur les classes ou les membres de l'API de la bibliothèque qui doivent rester intacts lorsqu'une bibliothèque elle-même est réduite avant distribution.@Keep(androidx.annotation.Keep) : à utiliser comme solution de repli lorsquekeepannon'est pas applicable. L'application de@Keepà une classe conserve la classe et tous ses membres de manière inconditionnelle. L'application de@Keepà une méthode ou à un champ agit comme un point d'entrée inconditionnel qui conserve le membre et sa classe conteneur, même si la classe n'est jamais instanciée, tandis quekeepannoexprime l'accessibilité conditionnelle.
Pour utiliser les annotations keepanno de R8 dans un module Soong :
Ajoutez
"keepanno-annotations"àlibsdansAndroid.bp:libs: [ "keepanno-annotations", ],Importez
com.android.tools.r8.keepanno.annotations.*et annotez la déclaration ou le site d'appel dans le code source Java ou Kotlin :import com.android.tools.r8.keepanno.annotations.KeepItemKind; import com.android.tools.r8.keepanno.annotations.UsedByReflection; public final class CustomPluginController { @UsedByReflection( description = "Instantiated via Class.forName from plugin config", kind = KeepItemKind.CLASS_AND_METHODS) public CustomPluginController(Context context) { // ... } }R8 traduit les annotations
keepannodirectement dans son modèle interne de règles de conservation et supprime les annotations de la sortie DEX finale, sans ajouter de surcharge de bytecode d'exécution.
Éviter les pièges courants
Les sections suivantes décrivent les pièges fréquents de proguard.flags et keep.xml dans le code de la plate-forme, et comment les corriger.
Règles de conservation des composants généraux
# Don't do this:
-keep class * extends android.app.Activity
-keep class * extends android.app.Service
-keep class * extends android.content.BroadcastReceiver
- Pourquoi cela nuit-il aux performances ? Cette règle est totalement redondante avec AAPT2.
Comme il utilise un caractère générique sans vérifier si le composant est enregistré dans le
AndroidManifest.xmlfusionné final, il force R8 à conserver chaque sous-classeActivity,ServiceouBroadcastReceivertrouvée n'importe où dans le classpath, y compris les composants de bibliothèque inutilisés et les activités de débogage désactivées. - Correction recommandée : supprimez la règle. AAPT2 inspecte le fichier manifeste fusionné et génère des règles
-keepexactes pour les composants enregistrés.
Règles de conservation générales pour le gestionnaire de clics XML
# Don't do this:
-keepclassmembers class * {
public void *(android.view.View);
}
- Pourquoi cela nuit-il aux performances ? : la conservation de chaque méthode
public void *(View)dans chaque classe du module empêche R8 de supprimer ou d'intégrer toute méthode qui accepte un seul paramètreView. - Correction recommandée : supprimez la règle. AAPT2 analyse les fichiers XML de mise en page et génère des règles de conservation ciblées pour les méthodes référencées par les attributs
android:onClick.
Conserver les règles pour les getters et setters Broad View
# Don't do this:
-keep public class * extends android.view.View {
public <init>(android.content.Context);
public <init>(android.content.Context, android.util.AttributeSet);
public <init>(android.content.Context, android.util.AttributeSet, int);
public void set*(...);
public *** get*();
}
- Pourquoi cela nuit-il aux performances ? Cette règle force R8 à conserver chaque getter et setter dans chaque sous-classe
Viewde l'application et dans toutes les bibliothèques associées (y compris les bibliothèques AndroidX et Material). Cela bloque la suppression des méthodes inutilisées et l'intégration du code de l'UI. - Correction recommandée : supprimez la règle. AAPT2 préserve déjà les constructeurs pour les classes
Viewpersonnalisées gonflées à partir de fichiers XML de mise en page. Si le code anime une propriété de vue à l'aide de noms de chaînes réflexifs tels queObjectAnimator.ofFloat(view, "translationZ", ...), remplacez le nom de chaîne par une référence de propriété typée (View.TRANSLATION_Zou une implémentationFloatPropertyouIntPropertypersonnalisée) pour éviter complètement la réflexion. Si l'accès aux propriétés par réflexion ne peut pas être évité, annotez le getter ou le setter spécifique avec@UsedByReflection.
Désactivation de l'optimisation ou de l'obscurcissement généralisés
# Don't do this in proguard.flags:
-dontoptimize
-dontshrink
-dontobfuscate
- Pourquoi cela nuit-il aux performances ? : placer
-dontoptimizeou-dontshrinkdans un fichier.flagsremplace silencieusement les paramètresAndroid.bpdu module et désactive les passes d'optimisation pour l'ensemble de la cible. Pire encore, si une bibliothèque exporte un fichier.flagscontenant-dontoptimize, elle désactive l'optimisation R8 pour chaqueandroid_appen aval qui associe la bibliothèque. - Solution recommandée : Supprimez
-dontoptimize,-dontshrinket-dontobfuscatedes fichiers.flags. Contrôlez explicitement le comportement d'optimisation dansAndroid.bpà l'aide du blocoptimize(shrink,optimize,obfuscate) sur la cible feuille.
Indicateurs de diagnostic dans les règles validées
# Don't do this in proguard.flags:
-verbose
-printmapping proguard.map
-printusage usage.txt
-printconfiguration config.txt
- Pourquoi cela nuit-il à la compilation ? Les indicateurs de diagnostic inondent les journaux de compilation ou tentent d'écrire des fichiers de sortie dans des chemins d'accès locaux lors des compilations Soong en bac à sable.
- Solution recommandée : Supprimez ces indicateurs des fichiers
.flagsvalidés. Soong écrit automatiquement les sorties de mappage et d'utilisation R8, telles queproguard_dictionaryetproguard_usage.zip, dans le répertoire intermédiaire du module sousout/soong/.intermediates/.
Règles de conservation de la production pour le code de test
- Pourquoi cela nuit aux performances : l'ajout de règles
-keeppersonnalisées uniquement pour l'accès aux tests oblige ce code à rester non obscurci et conservé dans les builds de production. Bien que l'annotation des méthodes avec@VisibleForTestingou la configuration detrace_references_fromévitent de gérer manuellement les règles-keep, ces symboles sont toujours inclus dans le binaire de production. - Solution recommandée : Pour que les points d'entrée réservés aux tests soient complètement exclus de l'APK de production, structurez l'application dans une bibliothèque
.implet associez-la à un APK de test d'auto-instrumentation à l'aide destatic_libs, comme décrit dans Étape 2 : Migrer les règles Keep axées sur les tests.
Caractères génériques de ressources globales dans keep.xml
<!-- Don't do this in res/raw/keep.xml: -->
<resources xmlns:tools="http://schemas.android.com/tools"
tools:keep="@raw/*,@drawable/*,@string/*" />
- Pourquoi cela nuit-il aux performances ? Les caractères génériques globaux conservent toutes les ressources du type correspondant dans le module et ses dépendances, ce qui annule la réduction des ressources (
shrink_resources: true) et augmente la taille de l'APK et de la tableresources.arscmappée. - Correction recommandée :
- Préférez les références de ressources statiques à
keep.xml: évitez d'utiliser la méthodeResources.getIdentifierpour rechercher dynamiquement un ensemble limité de ressources, telles que des chaînes de test numérotées ou des éléments drawable thématiques. Utilisez plutôt une instructionswitchau moment de la compilation ou mappez des constantes statiquesR.id,R.stringouR.drawable. Les références statiques permettent à R8 et AAPT2 de suivre les ressources en direct exactes, d'éliminer la surcharge de recherche de chaînes d'exécution et de supprimer complètement le besoin dekeep.xml. - Lister des noms de ressources spécifiques : si une recherche dynamique est requise, par exemple par un lecteur de licence tiers, listez les identifiants de ressources exacts dans
tools:keep(par exemple,tools:keep="@raw/third_party_licenses"). - Supprimez les fichiers
keep.xmlredondants : si les ressources listées sont déjà référencées de manière statique dans le code (R.raw.foo) ou le fichier XML (@raw/foo), l'outil de réduction les conserve automatiquement. Vous pouvez donc supprimerres/raw/keep.xml.
- Préférez les références de ressources statiques à
Vérifier et auditer les modifications apportées aux règles
Chaque fois que vous supprimez ou affinez des règles de conservation dans proguard.flags ou keep.xml, vérifiez que le module se compile correctement, qu'il réussit les tests unitaires et d'instrumentation, et qu'il conserve tous les points d'entrée requis.
Créer et tester des modules
Compilez le module de manière propre pour vérifier que R8 et AAPT2 se terminent sans avertissements de référence manquante :
m <MODULE_NAME>Exécutez des tests unitaires et d'instrumentation pour le module à l'aide de
atest:atest <TEST_MODULE_NAME>Pour les applications système, les services privilégiés ou les modules dépendants du matériel, exécutez des tests d'instrumentation et d'UI sur des appareils physiques cibles ou dans votre laboratoire de tests d'appareils pour exercer les chemins de réflexion d'exécution, les liaisons IPC et l'inflation des ressources.
Inspecter les différences entre les fichiers DEX et les ressources
Comparez l'APK ou le JAR compilés avant et après les modifications des règles pour confirmer que R8 supprime le code et les ressources inutilisés sans supprimer les points d'entrée attendus :
Utilisez
apkanalyzeroudexdumppour inspecter les classes, méthodes et champs conservés dans les fichiers APK ou DEX de sortie :apkanalyzer dex packages $OUT/system/priv-app/<APP_NAME>/<APP_NAME>.apkLorsque vous modifiez
keep.xmlou activezshrink_resources, utilisezaapt2 dump resourcespour vérifier que la compilation supprime les ressources inutilisées et conserve celles qui sont requises :aapt2 dump resources $OUT/system/priv-app/<APP_NAME>/<APP_NAME>.apk
Analyser le rayon de conservation et la subsomption des règles avec R8
Le compilateur R8 Open Source inclut un analyseur Keep Radius (également exposé dans Android Studio et Gradle sous le nom R8 Configuration Analyzer) qui mesure l'impact exact de chaque règle de conservation pendant la compilation. Soong intègre cet analyseur directement dans la compilation de la plate-forme Android (configurée dans build/soong/java/dex.go).
Pour analyser les règles de conservation pour un module de plate-forme ou pour l'ensemble d'une compilation :
Exécutez la compilation avec la variable d'environnement
R8_DUMP_KEEP_RADIUS=true:R8_DUMP_KEEP_RADIUS=true m <MODULE_NAME>Lorsque
R8_DUMP_KEEP_RADIUS=trueest défini, Soong demande à R8 d'enregistrer les métriques de règles keep dans un fichierr8keepradius.pbintermédiaire pour chaque module compilé sousout/soong/.intermediates/. Pour chaque règle de conservation et annotationkeepanno, R8 enregistre les informations suivantes :- Rayon de conservation immédiate : classes, champs et méthodes exacts conservés par la règle, ainsi que les contraintes spécifiques qu'elle applique contre la réduction, l'optimisation ou l'obscurcissement.
- Subsomption de règles : autres règles ou annotations de conservation qui conservent déjà les mêmes éléments. Si une règle personnalisée est entièrement englobée par une règle AAPT2 ou une règle de référence globale, vous pouvez la supprimer sans risque.
- Règles globales et à l'échelle du package : règles qui utilisent des caractères génériques à l'échelle du package ou qui appliquent des directives de configuration globales.
Convertissez la sortie
r8keepradius.pben rapport HTML interactif à l'aide deKeepRadiusHtmlReportGenerator(fourni avecprebuilts/r8/r8.jar) :# Generate an HTML report for a single module INTERMEDIATES=out/soong/.intermediates/packages/apps/<APP_NAME>/<APP_NAME> java -cp prebuilts/r8/r8.jar \ com.android.tools.r8.keepradius.KeepRadiusHtmlReportGenerator \ $INTERMEDIATES/android_common/r8keepradius.pb \ keep_radius_report.html # Scan all built modules under out/soong/.intermediates and generate # per-module HTML reports plus an aggregate keepradius.html summary java -cp prebuilts/r8/r8.jar \ com.android.tools.r8.keepradius.KeepRadiusHtmlReportGenerator \ out/soong/.intermediates \ out/keep_radius_reportsLorsqu'un répertoire est fourni,
KeepRadiusHtmlReportGeneratorparcourt tous les fichiers*keepradius*.pb, génère un rapport HTML pour chaque module et créeout/keep_radius_reports/keepradius.htmlrécapitulant le nombre d'éléments actifs, le nombre d'éléments conservés et les règles de conservation avec le rayon le plus élevé dans la compilation. Pour en savoir plus sur l'interprétation des scores de minification, d'optimisation et d'obscurcissement dans le rapport généré, consultez Utiliser l'analyseur de configuration R8.
Soong accepte également deux variables d'environnement de diagnostic R8 supplémentaires dans build/soong/java/dex.go :
R8_DUMP_INPUT=true: écritr8inputs.zipdans le répertoire intermédiaire du module contenant tous les fichiers JAR d'entrée, les fichiers JAR de bibliothèque et les configurations ProGuard fusionnées pour la reproduction R8 autonome.R8_DUMP_PERFETTO_TRACE=true: écritr8trace.ptracedans le répertoire intermédiaire du module pour inspecter les passes de compilation R8 dans Perfetto.
Valider les règles de consommateur de bibliothèque
Lorsqu'une exportation java_library ou android_library conserve des règles pour les consommateurs en aval (export_proguard_flags_files: true), ces règles ne doivent pas inclure d'indicateurs globaux qui modifient ou désactivent l'optimisation pour l'application consommatrice.
R8 fournit un analyseur de règles de conservation Open Source (ProcessKeepRules), exposé dans la compilation de la plate-forme Android en tant qu'outil hôte process-keep-rules (défini dans prebuilts/r8/Android.bp) :
# Build the R8 keep rules validator host binary
m process-keep-rules
# Validate one or more ProGuard configuration files
out/host/linux-x86/bin/process-keep-rules <PROGUARD_FLAGS_PATH>
L'outil process-keep-rules analyse chaque fichier de configuration et échoue avec des diagnostics de fichier et de ligne s'il rencontre des directives non autorisées dans les règles des consommateurs de bibliothèque. Les directives interdites incluent les directives de désactivation de l'optimisation globale, de la réduction ou de l'obscurcissement (telles que -dontoptimize ou -dontshrink), les indicateurs de reconditionnement et de modification de l'accès aux packages, les indicateurs de diagnostic ou de mappage, et -keepattributes au niveau de l'application.
Auditer les arborescences sources avec pgaudit.py
Pour auditer les répertoires sources non compilés en même temps que les analyseurs au moment de la compilation de R8, l'arborescence de la plate-forme Android inclut le script pgaudit.py sous build/make/core/proguard/tools/. Cet outil d'analyse statique analyse les fichiers Android.bp, proguard.flags et keep.xml d'un dépôt pour signaler les règles déjà couvertes par AAPT2 ou les références de plate-forme globales, les caractères génériques larges, les remplacements -dontoptimize et les règles de conservation réservées aux tests :
# Audit a single package directory
./build/make/core/proguard/tools/pgaudit.py packages/apps/Provision/
# Scan a subsystem tree and write a structured JSON report
./build/make/core/proguard/tools/pgaudit.py packages/ --json=audit_report.json
Les équipes de plate-forme et d'OEM peuvent combiner pgaudit.py pour un tri rapide de l'arborescence source, process-keep-rules pour valider les règles de bibliothèque exportées et R8_DUMP_KEEP_RADIUS=true avec KeepRadiusHtmlReportGenerator pour mesurer le rayon de rétention exact des classes, des champs et des méthodes de chaque règle dans une build.