ProGuard et R8 sont des outils d'obfuscation, de minification et d'optimisation pour les applications Android. ProGuard, créé en 2002, a longtemps été le standard de facto pour la protection du code Java. R8 est son successeur, développé par Google et intégré au plugin Android Gradle à partir d'AGP 3.4. Les deux outils réduisent la taille des APK, suppriment le code mort et compliquent l'ingénierie inverse. Selon Android Developers, R8 effectue les builds 2 à 3 fois plus rapidement que ProGuard avec une qualité d'obfuscation comparable.
Points Clés
ProGuard est un outil open source (Apache 2.0) pour l'obfuscation, la minification, l'optimisation et la prévérification du bytecode Java. Il a été développé par Eric Lafarge en 2002 dans le cadre du projet SourceForge. ProGuard prend en entrée des classes Java compilées (.class) ou des archives JAR et produit des classes traitées du même format, mais de taille réduite et avec des éléments renommés.
Pendant longtemps, ProGuard a été le seul standard pour protéger les applications Android contre l'ingénierie inverse. Google a officiellement recommandé son utilisation dans l'Android SDK et a fourni une configuration par défaut dans le fichier proguard-android-optimize.txt dans les outils SDK. ProGuard fonctionnait comme un outil séparé, lancé après la compilation du code Java en bytecode et avant le packaging en DEX.
ProGuard se compose de quatre phases séquentielles : shrink (suppression des classes inutilisées), optimize (optimisation du bytecode — inlining, suppression du code mort), obfuscate (renommage des classes, méthodes et champs en noms courts), preverify (vérification de compatibilité JVM). Chaque phase est contrôlée par des règles distinctes issues des fichiers de configuration.
Lors de la phase d'obfuscation, ProGuard génère un fichier de mapping (mapping.txt) qui fait correspondre les noms originaux aux noms obfusqués. Ce fichier est essentiel pour décoder les logs de crash des builds de release via l'utilitaire retrace. Sans fichier de mapping, une trace de pile devient un ensemble de lettres a(), b(), c() sans possibilité de restaurer le contexte d'origine.
| Phase ProGuard | Objectif | Résultat |
|---|---|---|
| Shrink | Analyse du graphe d'appels et suppression du code mort | Moins de classes dans l'APK |
| Optimize | Inlining de méthodes, suppression des paramètres inutilisés | Exécution du code plus rapide |
| Obfuscate | Renommage des classes, champs et méthodes | Protection contre l'ingénierie inverse |
| Preverify | Ajout d'attributs StackMap pour la JVM | Compatibilité Java 6+ |
R8 est un outil d'obfuscation et de minification de nouvelle génération de Google, présenté pour la première fois dans Android Studio 3.3 (novembre 2018) et devenu standard dans AGP 3.4 (août 2019). Contrairement à ProGuard, R8 fait partie du compilateur D8/R8 qui convertit le bytecode Java au format DEX. R8 effectue toutes les phases — obfuscation, minification et optimisation — en un seul passage, sans transmettre de fichiers intermédiaires entre les outils.
Google a développé R8 avec deux objectifs : accélérer les builds (ProGuard fonctionnait comme un outil externe) et assurer une intégration transparente avec la pile Android moderne (Desugar, Core Library Desugaring, D8). R8 est écrit en Kotlin et Java et fait partie du dépôt R8/Desugar sur AOSP (Android Open Source Project).
Un avantage important de R8 est la compatibilité ascendante complète avec les règles ProGuard. Les fichiers .pro existants fonctionnent sans modification. R8 prend même en charge les directives ProGuard spécifiques, notamment -whyareyoukeeping, -printconfiguration et -printmapping. Cela signifie que la transition de ProGuard à R8 est transparente : il suffit de mettre à jour AGP.
// build.gradle.kts — activation de R8 via minifyEnabled
android {
buildTypes {
getByName("release") {
isMinifyEnabled = true
isShrinkResources = true
proguardFiles(
// Configuration de base de l'Android SDK
getDefaultProguardFile("proguard-android-optimize.txt"),
// Règles personnalisées du projet
"proguard-rules.pro"
)
}
}
}Le code montre une configuration standard de build de release. Le flag isMinifyEnabled = true active R8 pour l'obfuscation et l'optimisation. isShrinkResources = true supprime en outre les ressources inutilisées. getDefaultProguardFile charge les règles par défaut du SDK, tandis que proguard-rules.pro contient les paramètres spécifiques au projet.
L'obfuscation est le processus de transformation du code source en une forme difficile à analyser par un humain mais qui conserve toutes les fonctionnalités. Dans le contexte Android, l'obfuscation signifie renommer les classes, méthodes et champs en noms courts et dénués de sens : com.example.app.auth.LoginManager devient a.a.a, la méthode authenticateUser devient a, le champ userToken devient b.
Les fichiers APK Android sont des archives qui peuvent être ouvertes avec n'importe quel archiveur (ZIP, 7z, WinRAR). Sans obfuscation, un attaquant obtient une carte complète de l'application : noms de packages, classes, méthodes et champs. Des outils comme jadx ou Bytecode Viewer peuvent restaurer du code Java quasi original à partir de fichiers DEX en quelques secondes. L'obfuscation ne rend pas le code invulnérable, mais elle élève considérablement la barrière d'entrée : au lieu de noms significatifs, le lecteur voit a(), b(), c().
Objectifs typiques de l'obfuscation : protéger la logique commerciale (algorithmes, formules de calcul), entraver le vol de clés API et de tokens, empêcher la substitution de classes par réflexion, et éviter la modification et le repackaging d'APK (attaque de repackage). En pratique, 70 % des tâches sont résolues par le simple renommage — c'est pourquoi ProGuard/R8 sont utilisés.
Voici un fichier proguard-rules.pro typique pour un projet Android avec Retrofit, Gson et Parcelable. Les règles -keep préservent les classes et méthodes nécessaires au fonctionnement des bibliothèques via la réflexion. Sans ces règles, R8 supprimera ou renommera les classes auxquelles la bibliothèque accède par nom de chaîne.
# =====================
# Retrofit — préservation des interfaces
# =====================
-keep,allowobfuscation,allowshrinking interface retrofit2.** { *; }
-keepattributes Signature, Exceptions
# =====================
# Gson — sérialisation JSON
# =====================
-keepclassmembers class * {
@com.google.gson.annotations.SerializedName <fields>;
}
-keep class *.serialization.** {
<fields>;
}
# =====================
# Parcelable — Creator
# =====================
-keepclassmembers class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator CREATOR;
}
# =====================
# Logging — suppression des logs de la release
# =====================
-assumenosideeffects class android.util.Log {
public static boolean isLoggable(String, int);
public static int v(...);
public static int d(...);
public static int i(...);
public static int w(...);
public static int e(...);
}
# =====================
# Classes de données Kotlin — préservation des constructeurs
# =====================
-keepclassmembers class * {
@kotlin.Metadata <fields>;
}
# =====================
# Activity — point d'entrée
# =====================
-keep class * extends android.app.Activity {
@android.annotation.SuppressLint <methods>;
}Chaque directive dans un fichier .pro résout une tâche spécifique. -keep empêche la classe entière d'être supprimée ou renommée. -keepclassmembers protège uniquement les membres de la classe (champs et méthodes) mais permet à la classe elle-même d'être supprimée si elle n'est pas utilisée. -assumenosideeffects indique à R8 qu'un appel de méthode n'a pas d'effets secondaires et peut être supprimé en toute sécurité. La directive -keepattributes préserve les métadonnées dans le bytecode — annotations, signatures, exceptions.
La règle -keep,allowobfuscation,allowshrinking pour Retrofit permet à R8 de renommer les interfaces mais pas de les supprimer. Ceci est nécessaire car Retrofit accède aux interfaces via des proxies dynamiques (java.lang.reflect.Proxy), et la suppression entraînerait une ClassNotFoundException à l'exécution. De même, Gson utilise la réflexion pour accéder aux champs annotés avec @SerializedName — sans -keepclassmembers, les champs seront supprimés comme inutilisés.
La minification (shrinking) est le processus de suppression du code et des ressources inutilisés du build final. ProGuard et R8 analysent le graphe d'appels à partir des points d'entrée (Activity, Service, BroadcastReceiver) et suppriment les classes et méthodes qui ne peuvent pas être atteintes via la chaîne d'appels. ShrinkResources est une étape supplémentaire qui supprime les ressources inutilisées de res/ (layout, drawable, string, color).
La minification offre le plus grand bénéfice dans les grands projets avec des bibliothèques. Un scénario typique : un projet utilise 10 % du code d'une bibliothèque connectée (par exemple Google Play Services). Sans minification, tout le code de la bibliothèque se retrouve dans l'APK. Avec la minification, R8 supprime 70 à 90 % du code de la bibliothèque, ne laissant que les classes et méthodes réellement utilisées. Cela impacte directement la taille de l'APK, le temps de chargement et la consommation mémoire.
Le mécanisme ShrinkResources fonctionne en tandem avec la minification du code. Après que R8 a déterminé quelles classes sont utilisées, la réduction des ressources analyse les références aux ressources depuis le code : R.layout.main, R.drawable.icon, getString(R.string.title). Toutes les ressources sans référence directe ou indirecte sont supprimées de l'APK ou de l'AAB final. Cela se fait à l'aide du fichier de ressources resources.arsc et des dossiers res/.
Une nuance importante : les ressources peuvent être accédées via getIdentifier() ou Resources.getResourceName() par nom de chaîne, contournant la classe R. Dans ces cas, R8 ne voit pas de lien direct et peut supprimer une ressource qui est réellement utilisée. Pour protéger ces ressources, il existe la directive -keep class **.R$* { *; } — elle préserve tous les identifiants de la classe R.
<!-- Exemple : ressource utilisée uniquement via getIdentifier() -->
<string name="dynamic_title_welcome">Bienvenue</string>
<string name="dynamic_title_share">Partager</string>
<!-- Code Kotlin accédant par chaîne -->
<!-- val title = getString(resources.getIdentifier( -->
<!-- \"dynamic_title_${type}\", \"string\", packageName)) -->Dans ce cas, R8 ne voit pas de référence statique à dynamic_title_welcome dans la classe R car l'accès se fait via getIdentifier avec un nom dynamique. Pour préserver ces ressources, ajoutez la directive -keepclassmembers class **.R$string { *; } à proguard-rules.pro — elle empêche la suppression de tous les champs de toutes les classes R$string.
| Directive | Objectif | Exemple |
|---|---|---|
| -keep | Préserve la classe et tous ses membres | -keep class com.example.api.** { *; } |
| -keepclassmembers | Préserve uniquement les membres de la classe | -keepclassmembers class * { @SerializedName <fields>; } |
| -keepattributes | Préserve les métadonnées du bytecode | -keepattributes *Annotation*, Signature |
| -assumenosideeffects | Supprime les appels sans effets secondaires | -assumenosideeffects class Log { d(...); } |
| -dontwarn | Supprime les avertissements | -dontwarn com.example.legacy.** |
Bien que R8 soit le successeur de ProGuard, il existe des différences fondamentales entre les outils en termes d'architecture, de performances et de comportement. Google a officiellement arrêté le support de ProGuard dans le plugin Android Gradle à partir d'AGP 7.0, mais ProGuard continue d'être utilisé dans les projets nécessitant un comportement d'optimisation spécifique non disponible dans R8.
| Caractéristique | ProGuard | R8 |
|---|---|---|
| Développeur | GuardSquare (Eric Lafarge) | |
| Année de sortie | 2002 | 2018 (stable en 2019) |
| Architecture | 4 phases séparées (shrink → optimize → obfuscate → preverify) | Un seul passage : shrink + optimize + obfuscate simultanément |
| Intégration dans AGP | Outil externe, lancé après javac | Intégré dans le compilateur D8 DEX |
| Vitesse de build | 2 à 3 fois plus lent | Plus rapide grâce au passage unique et à l'intégration native |
| Support Kotlin | Limité (problèmes avec inline, lambdas, coroutines) | Complet : coroutines, fonctions inline, data class |
| Fichier de mapping | mapping.txt (compatible avec retrace) | mapping.txt (même format) |
| Personnalisation de l'optimisation | 60+ options -optimizationpasses, -optimizations | Limitée : la plupart des optimisations activées par défaut |
| Statut du support | Remplacé par R8 (AGP 7.0+ ne l'utilise pas) | Développement actif, partie d'AOSP |
R8 est plus agressif que ProGuard dans la suppression du code qu'il considère comme mort. Cela conduit à des situations où le build de débogage fonctionne mais le build de release échoue avec ClassNotFoundException ou NoSuchMethodException. Cas typiques : bibliothèques utilisant la réflexion par nom de classe (Gson, Moshi, Retrofit, Room, Dagger) ; appels ServiceLoader ou java.util.ServiceLoader ; proxies dynamiques (java.lang.reflect.Proxy) ; méthodes natives (JNI). La solution est d'ajouter -keep pour toutes les classes appelées via la réflexion.
# Problèmes typiques de réflexion — R8 ne voit pas la liaison statique
# Room — préservation des DAO et migrations
-keep class * extends androidx.room.RoomDatabase { *; }
-keep class *.DatabaseMigrations { *; }
# Dagger / Hilt — préservation des composants
-keep class * extends dagger.hilt.android.components.** { *; }
# JNI — ne pas renommer les méthodes natives
-keepclasseswithmembernames class * {
native <methods>;
}
# Data Binding — préservation des classes Binding
-keep class *.databinding.** { *; }Si après l'ajout des règles le build échoue toujours, utilisez le flag -printconfiguration full-config.txt dans proguard-rules.pro. R8 générera un fichier de configuration complet montrant quelles règles sont appliquées et quelles classes sont préservées. La directive -whyareyoukeeping class com.example.MyClass est également utile — elle affiche la raison pour laquelle R8 a décidé de préserver la classe en question.
La configuration appropriée des règles ProGuard est la clé d'une obfuscation stable sans bogues d'exécution. Voici un processus de configuration étape par étape pour un nouveau projet ou pour un projet où l'obfuscation provoque des erreurs.
Commencez par inclure le fichier standard de l'Android SDK — proguard-android-optimize.txt. Il contient des règles pour les composants Android de base : Activity, Service, BroadcastReceiver, ContentProvider, View, Fragment. Ce fichier se trouve dans le dossier SDK : $ANDROID_HOME/tools/proguard/proguard-android-optimize.txt. Si vous utilisez AGP, getDefaultProguardFile le chargera automatiquement.
Chaque bibliothèque populaire a des règles ProGuard recommandées. Retrofit, OkHttp, Glide, Fresco, Coil, Room, Dagger/Hilt, Kotlin Coroutines — toutes nécessitent des règles -keep spécifiques. Généralement, les règles sont incluses dans la bibliothèque AAR et sont liées automatiquement via les règles de consommateur. Vérifiez que la bibliothèque fournit un fichier proguard.txt dans l'AAR — cela indique que les règles sont déjà prises en compte.
Avant la publication, assurez-vous de tester le build de release sur un appareil réel ou un émulateur. Les problèmes d'obfuscation ne se manifestent qu'à l'exécution. Vérifiez : l'authentification (connexion/inscription), le chargement des données depuis le réseau, la navigation entre les écrans, l'appareil photo et la galerie, les notifications push, les Deeplinks, WebView. Chaque crash dans le build de release doit être décodé avec retrace à l'aide du fichier de mapping, et les règles -keep manquantes doivent être ajoutées.
Le fichier de mapping est généré dans build/outputs/mapping/release/mapping.txt. Ce fichier doit être conservé : sans lui, il est impossible de décoder les logs de crash de la Google Play Console. Incluez mapping.txt dans votre système de contrôle de version ou téléchargez-le comme artefact CI. La Google Play Console accepte le fichier de mapping automatiquement lors du téléchargement d'un AAB avec uploading mapping.txt activé.
Voici un workflow complet de configuration de l'obfuscation dans le fichier proguard-rules.pro avec des commentaires pour chaque groupe de règles.
# ===========================================
# proguard-rules.pro — exemple complet
# ===========================================
# --- Paramètres généraux ---
-keepattributes *Annotation*, Signature, Exceptions, InnerClasses, EnclosingMethod
-dontpreverify
# --- Composants Android ---
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.Fragment
-keep public class * extends androidx.fragment.app.Fragment
-keep public class * extends android.view.View
# --- OkHttp / Retrofit ---
-dontwarn okhttp3.**
-dontwarn okio.**
-keep class retrofit2.** { *; }
-keepattributes Exceptions
# --- Gson / Moshi ---
-keepclassmembers class * {
@com.google.gson.annotations.SerializedName <fields>;
}
-keep class com.google.gson.** { *; }
# --- Firebase ---
-keep class com.google.firebase.** { *; }
-keep class com.google.android.gms.** { *; }
# --- Kotlin Coroutines ---
-keepnames class kotlinx.coroutines.internal.MainDispatcherFactory {}
-keepnames class kotlinx.coroutines.CoroutineExceptionHandler {}
# --- Sérialisation ---
-keepclassmembers class * implements java.io.Serializable {
private static final java.io.ObjectStreamField[] serialPersistentFields;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
}
# --- R8 uniquement : préservation forcée ---
# (ProGuard ignore cette directive)
-keep,allowobfuscation class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator CREATOR;
}Après la configuration, exécutez le build : ./gradlew assembleRelease. Vérifiez que des fichiers sont apparus dans build/outputs/mapping/release/ : mapping.txt (correspondance des noms originaux vers les noms obfusqués), seeds.txt (classes préservées par les règles -keep), usage.txt (classes supprimées lors de la minification). La taille de l'APK après obfuscation devrait diminuer de 20 à 50 % selon le nombre de bibliothèques connectées.
Foire Aux Questions
R8 est le successeur de ProGuard, développé par Google. R8 effectue l'obfuscation, la minification et l'optimisation en un seul passage, fonctionne 2 à 3 fois plus vite que ProGuard et est intégré directement dans le plugin Android Gradle. ProGuard utilise quatre phases séparées et nécessite une exécution externe. À partir d'AGP 7.0, ProGuard n'est plus utilisé — R8 fonctionne par défaut.
Oui, R8 utilise les mêmes règles ProGuard (fichiers .pro). Les directives -keep, -keepclassmembers, -keepattributes, -assumenosideeffects fonctionnent à l'identique. Les règles de base se trouvent dans proguard-android-optimize.txt de l'Android SDK, tandis que les règles spécifiques aux bibliothèques (Retrofit, Room, Gson) sont ajoutées dans proguard-rules.pro du projet. Sans ces règles, R8 peut supprimer les classes nécessaires aux bibliothèques fonctionnant par réflexion.
R8 est activé par défaut dans le plugin Android Gradle depuis AGP 3.4. Pour activer la minification, définissez isMinifyEnabled = true dans le bloc release buildType du fichier build.gradle.kts. Le flag supplémentaire isShrinkResources = true active la suppression des ressources inutilisées. Dans gradle.properties, vous pouvez désactiver R8 de force via android.enableR8=false, mais ce n'est pas recommandé — R8 est plus rapide et plus stable.
L'obfuscation — renommage des classes, méthodes et champs en noms courts dénués de sens (a, b, c). La classe com.example.app.auth.LoginManager devient a.a.a, la méthode authenticateUser devient a. Cela complique l'ingénierie inverse de l'application mais n'affecte pas la logique d'exécution. ProGuard et R8 ne renomment que les éléments non protégés par les règles -keep. Le fichier de mapping préserve la correspondance des noms originaux et obfusqués pour décoder les logs de crash.
Pour décoder une trace de pile, utilisez l'utilitaire retrace (fait partie du SDK ProGuard/R8). Commande : retrace mapping.txt crash-stacktrace.txt. Le fichier de mapping se trouve dans build/outputs/mapping/release/mapping.txt. La Google Play Console supporte également le téléchargement de mapping.txt lors de la publication d'un AAB — les logs de crash sont automatiquement décodés dans la console. Sans fichier de mapping, la trace de pile ne contiendra que des noms obfusqués comme a.b.c(), ce qui est inutile pour le débogage.
Résumé
Nous développerons une application mobile clé en main
IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.
Lisez aussi