Configurer et dépanner les règles de conservation R8
Temps de lecture : 7 min
Dans le développement Android moderne, les utilisateurs s'attendent à ce que les applications soient légères, rapides et sécurisées. Le principal outil du système de compilation Android pour y parvenir est l'optimiseur R8 , le compilateur qui gère la suppression du code et des ressources inutilisés pour la minification, le renommage ou la minification du code, et l'optimisation de l'application.
L'activation de R8 est une étape essentielle de la préparation d'une application pour sa publication, mais elle nécessite que les développeurs fournissent des instructions sous la forme de "règles de conservation".
Après avoir lu cet article, regardez la vidéo de la semaine Performance Spotlight sur l'activation, le débogage et le dépannage de l'optimiseur R8 sur YouTube.
Pourquoi les règles de conservation sont-elles nécessaires ?
La nécessité d'écrire des règles de conservation découle d'un conflit fondamental : R8 est un outil d'analyse statique, mais les applications Android s'appuient souvent sur des modèles d'exécution dynamique tels que la réflexion ou les appels vers et depuis du code natif à l'aide de l'interface JNI (Java Native Interface).
R8 crée un graphique du code utilisé en analysant les appels directs. Lorsque le code est accédé de manière dynamique, l'analyse statique de R8 ne peut pas le prédire. Elle l'identifie comme inutilisé et le supprime, ce qui entraîne des plantages au moment de l'exécution.
Une règle de conservation est une instruction explicite adressée au compilateur R8, qui indique : "Cette classe, méthode ou champ spécifique est un point d'entrée auquel on accède de manière dynamique au moment de l'exécution. Vous devez le conserver, même si vous ne trouvez pas de référence directe à celui-ci."
Pour en savoir plus sur les règles de conservation, consultez le guide officiel.
Où rédiger des règles de conservation
Les règles de conservation personnalisées d'une application sont écrites dans un fichier texte. Par convention, ce fichier est nommé proguard-rules.pro et se trouve à la racine de l'application ou du module de bibliothèque. Ce fichier est ensuite spécifié dans le type de compilation release du fichier build.gradle.kts de votre module.
release {
isShrinkResources = true
isMinifyEnabled = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro",
)
}Utiliser le fichier par défaut approprié
La méthode getDefaultProguardFile importe un ensemble de règles par défaut fournies par le SDK Android. Si vous utilisez le mauvais fichier, votre application risque de ne pas être optimisée. Veillez à utiliser proguard-android-optimize.txt. Ce fichier fournit les règles de conservation par défaut pour les composants Android standards et active les optimisations de code de R8. L'ancien proguard-android.txt ne fournit que les règles de conservation, mais n'active pas les optimisations de R8.
Comme il s'agit d'un problème de performances grave, nous commençons à avertir les développeurs de l'utilisation d'un fichier incorrect, à partir de la mise à jour de fonctionnalités Android Studio Narwhal 3. À partir de la version 9.0 du plug-in Android Gradle, nous ne prendrons plus en charge le fichier proguard-android.txt obsolète. Veillez donc à passer à la version optimisée.
Rédiger des règles de conservation
Une règle de conservation se compose de trois parties principales :
- Une option comme
-keepou-keepclassmembers - Modificateurs facultatifs tels que
allowshrinking - Une spécification de classe qui définit le code à mettre en correspondance
Pour connaître la syntaxe complète et voir des exemples, consultez les instructions pour ajouter des règles de conservation.
Antimodèles de règles de conservation
Il est important de connaître les bonnes pratiques, mais aussi les anti-modèles. Ces anti-modèles découlent souvent d'une mauvaise compréhension ou de raccourcis de dépannage, et peuvent être catastrophiques pour les performances d'un build de production.
Options globales
Ces options sont des boutons d'activation/de désactivation globaux qui ne doivent jamais être utilisés dans un build de version. Elles ne sont destinées qu'à un débogage temporaire pour isoler un problème.
L'utilisation de -dontotptimize désactive efficacement les optimisations des performances de R8, ce qui ralentit l'application.
Lorsque vous utilisez -dontobfuscate, vous désactivez tout changement de nom, et l'utilisation de -dontshrink désactive la suppression du code mort. Ces deux règles globales augmentent la taille de l'application.
Dans la mesure du possible, évitez d'utiliser ces options globales dans un environnement de production pour offrir une expérience utilisateur plus performante.
Règles de conservation trop générales
Le moyen le plus simple d'annuler les avantages de R8 est d'écrire des règles de conservation trop larges. Les règles de conservation telles que celle ci-dessous indiquent à l'optimiseur R8 de ne pas réduire, obscurcir ni optimiser aucune classe de ce package ni aucun de ses sous-packages. Cela supprime complètement les avantages de R8 pour l'ensemble de ce package. Essayez plutôt de rédiger des règles de conservation précises et spécifiques.
-keep class com.example.package.** { *;} // WIDE KEEP RULES CAUSE PROBLEMSL'opérateur d'inversion (!)
L'opérateur d'inversion (!) semble être un moyen efficace d'exclure un package d'une règle. Mais ce n'est pas si simple. Prenons un exemple :
-keep class !com.example.my_package.** { *; } // USE WITH CAUTIONVous pourriez penser que cette règle signifie "ne pas conserver les classes danscom.example.package". En réalité, elle signifie "conserver toutes les classes, méthodes et propriétés dans l'ensemble de l'application qui ne se trouvent pas dans com.example.package". Si cela vous surprend, vérifiez s'il existe des négations dans votre configuration R8.
Règles redondantes pour les composants Android
Une autre erreur courante consiste à ajouter manuellement des règles de conservation pour les éléments Activities, Services ou BroadcastReceivers de votre application. Ce n'est pas nécessaire. Le fichier proguard-android-optimize.txt par défaut inclut déjà les règles pertinentes pour que ces composants Android standards fonctionnent sans configuration supplémentaire.
De plus, de nombreuses bibliothèques apportent leurs propres règles de conservation. Vous ne devriez donc pas avoir à écrire vos propres règles pour ces cas. En cas de problème avec les règles de conservation d'une bibliothèque que vous utilisez, il est préférable de contacter l'auteur de la bibliothèque pour savoir d'où vient le problème.
Bonnes pratiques concernant les règles de conservation
Maintenant que vous savez ce qu'il ne faut pas faire, parlons des bonnes pratiques.
Rédiger des règles de conservation précises
Les bonnes règles de conservation doivent être aussi précises et spécifiques que possible. Elles ne doivent conserver que ce qui est nécessaire, ce qui permet à R8 d'optimiser tout le reste.
| Règle | Qualité |
|---|---|
| Faible : conserve un package entier et ses sous-packages. |
| Faible : conserve une classe entière, qui est probablement encore trop large. |
-keepclassmembers class com.example.MyClass {
private java.lang.String secretMessage;
public void onNativeEvent(java.lang.String);
} | Élevé : seules les méthodes et propriétés pertinentes d'une classe spécifique sont conservées. |
Utiliser des ancêtres communs
Au lieu d'écrire des règles de conservation distinctes pour plusieurs modèles de données différents, écrivez une règle qui cible une classe ou une interface de base commune. La règle ci-dessous indique à R8 de conserver tous les membres des classes qui implémentent cette interface. Elle est hautement évolutive.
# Keep all fields of any class that implements SerializableModel
-keepclassmembers class * implements com.example.models.SerializableModel {
<fields>;
}Utiliser des annotations pour cibler plusieurs classes
Créez une annotation personnalisée (par exemple, @Serialize) et utilisez-la pour "taguer" les classes dont les champs doivent être conservés. Il s'agit d'un autre modèle propre, déclaratif et hautement évolutif. Vous pouvez également créer des règles de conservation pour les annotations existantes des frameworks que vous utilisez.
# Keep all fields of any class annotated with @Serialize
-keepclassmembers class * {
@com.example.annotations.Serialize <fields>;
}Choisir la bonne option de conservation
L'option "Conserver" est l'élément le plus important de la règle. Si vous choisissez la mauvaise option, l'optimisation risque d'être désactivée inutilement.
| Option "Conserver" | Fonction |
-keep | Empêche la suppression ou le changement de nom de la classe et des membres mentionnés dans la déclaration . |
-keepclassmembers | Empêche la suppression ou le changement de nom des membres spécifiés, mais autorise la suppression de la classe elle-même, uniquement pour les classes qui ne sont pas supprimées d'une autre manière. |
-keepclasseswithmembers | Combinaison : conserve la classe et ses membres, uniquement si tous les membres spécifiés sont présents. |
Pour en savoir plus sur l'option de conservation, consultez notre documentation sur les options de conservation.
Autoriser l'optimisation avec des modificateurs
Les modificateurs tels que allowshrinking et allowobfuscation assouplissent une règle -keep large, ce qui redonne à R8 le pouvoir d'optimiser. Par exemple, si une ancienne bibliothèque vous oblige à utiliser -keep sur une classe entière, vous pouvez récupérer une partie de l'optimisation en autorisant la minification et l'obscurcissement :
# Keep this class, but allow R8 to remove it if it's unused and allow R8 to rename it. -keep,allowshrinking,allowobfuscation class com.example.LegacyClass
Ajouter des options globales pour une optimisation supplémentaire
En plus des règles de conservation, vous pouvez ajouter des options globales à votre fichier de configuration R8 pour encourager encore plus d'optimisation.
-repackageclasses est une option puissante qui indique à R8 de déplacer toutes les classes obscurcies dans un seul package. Cela permet d'économiser beaucoup d'espace dans le fichier DEX en supprimant les chaînes de nom de package redondantes.
-allowaccessmodification permet à R8 d'élargir l'accès (par exemple, de private à public) pour permettre une intégration plus agressive. Cette option est désormais activée par défaut lorsque vous utilisez proguard-android-optimize.txt.
Avertissement : Les auteurs de bibliothèques ne doivent jamais ajouter ces options d'optimisation globale à leurs règles de consommation, car elles seraient appliquées de force à l'ensemble de l'application.
Pour que ce soit encore plus clair, dans la version 9.0 du plug-in Android Gradle, nous allons commencer à ignorer complètement les options d'optimisation globale des bibliothèques.
Bonnes pratiques pour les bibliothèques
Chaque application Android s'appuie sur des bibliothèques d'une manière ou d'une autre. Voyons ensemble les bonnes pratiques pour les bibliothèques.
Pour les développeurs de bibliothèques
Si votre bibliothèque utilise la réflexion ou JNI, vous devez fournir les règles de conservation nécessaires à ses consommateurs. Ces règles sont placées dans un fichier consumer-rules.pro, qui est ensuite automatiquement intégré au fichier AAR de la bibliothèque.
android {
defaultConfig {
consumerProguardFiles("consumer-rules.pro")
}
...
}Pour les utilisateurs de la bibliothèque
Filtrer les règles de conservation problématiques
Si vous devez utiliser une bibliothèque qui inclut des règles de conservation problématiques, vous pouvez les filtrer dans votre fichier build.gradle.kts à partir d'AGP 9.0. Cela indique à R8 d'ignorer les règles provenant d'une dépendance spécifique.
release {
optimization.keepRules {
// Ignore all consumer rules from this specific library
it.ignoreFrom("com.somelibrary:somelibrary")
}
}La meilleure règle de conservation est l'absence de règle de conservation
La stratégie de configuration R8 ultime consiste à supprimer complètement la nécessité d'écrire des règles de conservation. Pour de nombreuses applications, cela peut être réalisé en choisissant des bibliothèques modernes qui privilégient la génération de code plutôt que la réflexion. Avec la génération de code, l'optimiseur peut déterminer plus facilement quel code est réellement utilisé au moment de l'exécution et quel code peut être supprimé. De plus, l'absence de réflexion dynamique signifie qu'il n'y a pas de points d'entrée "masqués" et qu'aucune règle de conservation n'est donc nécessaire. Lorsque vous choisissez une nouvelle bibliothèque, privilégiez toujours une solution qui utilise la génération de code plutôt que la réflexion.
Pour en savoir plus sur le choix des bibliothèques, consultez Choisir judicieusement les bibliothèques.
Déboguer et dépanner votre configuration R8
Si R8 supprime du code qu'il aurait dû conserver ou si votre APK est plus volumineux que prévu, utilisez ces outils pour diagnostiquer le problème.
Rechercher les règles Keep en double et globales
Comme R8 fusionne des règles provenant de dizaines de sources, il peut être difficile de savoir quel est l'ensemble de règles "final". L'ajout de cette option à votre fichier proguard-rules.pro génère un rapport complet :
# Outputs the final, merged set of rules to the specified file -printconfiguration build/outputs/logs/configuration.txt
Vous pouvez effectuer une recherche dans ce fichier pour trouver des règles redondantes ou remonter jusqu'à la bibliothèque spécifique qui a inclus une règle problématique (comme -dontoptimize).
Demander à R8 : Pourquoi gardez-vous cela ?
Si une classe que vous pensiez avoir supprimée est toujours présente dans votre application, R8 peut vous expliquer pourquoi. Il vous suffit d'ajouter cette règle :
# Asks R8 to explain why it's keeping a specific class class com.example.MyUnusedClass -whyareyoukeeping
Pendant la compilation, R8 affiche la chaîne exacte de références qui l'a amené à conserver cette classe, ce qui vous permet de tracer la référence et d'ajuster vos règles.
Pour obtenir un guide complet, consultez la section Résoudre les problèmes liés à R8.
Étapes suivantes
R8 est un outil puissant pour améliorer les performances des applications Android. Son efficacité dépend d'une bonne compréhension de son fonctionnement en tant que moteur d'analyse statique.
En écrivant des règles spécifiques au niveau des membres, en exploitant les ancêtres et les annotations, et en choisissant soigneusement les bonnes options de conservation, vous pouvez conserver exactement ce qui est nécessaire. La pratique la plus avancée consiste à éliminer complètement le besoin de règles en choisissant des bibliothèques modernes basées sur la génération de code plutôt que leurs prédécesseurs basés sur la réflexion.
Pendant la Performance Spotlight Week, n'oubliez pas de regarder la vidéo Spotlight Week du jour sur YouTube et de continuer notre défi R8. Utilisez #optimizationEnabled pour toute question sur l'activation ou le dépannage de R8. Nous sommes là pour vous aider.
Il est temps de découvrir les avantages par vous-même.
Nous vous mettons au défi d'activer le mode complet de R8 pour votre application dès aujourd'hui.
- Suivez nos guides du développeur pour commencer : Activer l'optimisation des applications.
- Vérifiez si vous utilisez toujours
proguard-android.txtet remplacez-le parproguard-android-optimize.txt. - Ensuite, mesurez l'impact. Ne vous contentez pas de ressentir la différence, vérifiez-la. Mesurez vos gains de performances en adaptant le code de notre exemple d'application Macrobenchmark sur GitHub pour mesurer vos temps de démarrage avant et après.
Nous sommes convaincus que vous constaterez une amélioration significative des performances de votre application.
Profitez-en pour poser vos questions avec le hashtag #AskAndroid. Tout au long de la semaine, nos experts surveillent et répondent à vos questions.
Revenez demain pour découvrir l'optimisation guidée par les profils avec les profils de référence et de démarrage, l'amélioration des performances de rendu de Compose au fil des dernières versions et les considérations de performances pour les tâches en arrière-plan.
-
Actualités sur les produitsÀ mesure que le développement agentique s'accélère, l'ingénierie Android atteint une nouvelle frontière passionnante : passer de la complétion de code assistée à des agents qui s'exécutent de bout en bout sous votre direction. Vous disposez d'un choix et d'une flexibilité totaux concernant les modèles et les agents que vous utilisez.
Jason Buchel • Temps de lecture : 3 min -
Actualités sur les produitsAujourd'hui, nous avons expliqué comment nous faisions évoluer notre vision "plus qu'un magasin". De l'onglet "Vous", qui sert désormais d'espace personnel à plus de 250 millions d'utilisateurs actifs par mois, à notre plate-forme de jeux unifiée qui connecte plus de 700 millions de profils de joueurs, Google Play répond aux besoins des utilisateurs où qu'ils se trouvent.
Paul Feng • Temps de lecture : 3 min -
Actualités sur les produitsAvec Googlebook, nous sommes ravis d'aider les utilisateurs à s'exprimer et à accéder à ces outils.
Cyrus Stoller • Temps de lecture : 2 min
Recevez chaque semaine les dernières informations sur le développement Android dans votre boîte de réception.