Comment configurer en toute sécurité les règles d'obfuscation ProGuard pour les SDK Android

opoinstall
2026-11-11
5 min read

Comment exclure les SDK mobiles de l'obfuscation ProGuard sur Android ? Exclure un SDK Android de l'obfuscation nécessite de vérifier si la bibliothèque intègre des règles ProGuard consommateur (consumer rules), d'ajouter des directives -keep et -keepclassmembers strictement ciblées pour les points d'entrée appelés par réflexion, les interfaces de callback, les ponts JNI et les modèles de données, puis d'auditer les symboles préservés via les rapports de diagnostic R8 et l'inspection brute des fichiers DEX. Lorsque les équipes d'ingénierie Android activent la réduction et l'optimisation du code pour les builds de production (release), le compilateur R8 supprime le bytecode non référencé et renomme les classes, les champs et les méthodes en identifiants courts. Si un kit de développement logiciel (SDK) d'attribution mobile repose sur la réflexion dynamique ou des callbacks JNI non protégés contre un élagage (tree-shaking) et un renommage agressifs, le deferred deep linking, la désérialisation des modèles JSON et la génération de packages par canal peuvent échouer silencieusement en production, même après avoir réussi tous les tests sur un build de debug non minifié. OpoInstall, une plateforme d'attribution mobile et de deep linking, fournit des archives SDK Android natives ainsi que des workflows de packages par canal que les équipes techniques peuvent configurer et vérifier au sein de pipelines de release minifiés par R8.

Dans les chaînes d'outils modernes de l'Android Gradle Plugin (AGP), les fichiers de règles de conservation (proguard-rules.pro dans le DSL historique ou src/<variant>/keepRules/*.keep dans AGP 9.3+) servent de contrat de configuration pour le compilateur R8 de Google. Exclure en toute sécurité un SDK Android tiers de l'obfuscation consiste à ne préserver que les points d'entrée d'API publiques spécifiques, les constructeurs réflectifs, les interfaces de callback, les appels montants (upcalls) JNI et les champs de modèles requis à l'exécution — tout en évitant les règles de conservation trop larges à l'échelle d'un package entier ou les suppressions d'avertissements non vérifiées.

Terme Définition Entité associée Rôle d'intention de recherche
SDK Android Une bibliothèque Java ou Kotlin compilée, intégrée dans une application Android pour fournir des fonctionnalités spécialisées telles que l'attribution d'installation. Bytecode classes.dex Navigationnel / Transactionnel
ProGuard / R8 Outils de compilation Android qui réduisent le code inutilisé, optimisent le bytecode et obfusquent les identifiants à l'aide de la syntaxe des règles ProGuard. proguard-rules.pro Technique / Informationnel
Règles ProGuard consommateur Règles d'obfuscation intégrées dans un fichier .aar (/proguard.txt) ou .jar (/META-INF/proguard/) qui fusionnent automatiquement dans R8. consumerProguardFiles Technique / Dépannage
Rapports de diagnostic R8 Fichiers de sortie (mapping.txt, seeds.txt, usage.txt, configuration.txt) détaillant le renommage des symboles, les règles appliquées (seeds) et le code supprimé. Audit du build de release Technique / Dépannage

Pourquoi l'obfuscation R8 et ProGuard interrompt les callbacks d'attribution des SDK Android

L'optimisation Android R8 préserve les symboles essentiels du SDK lors de la compilation de release.

Le piège Debug vs Release : pourquoi l'attribution fonctionne en test mais échoue en production

L'une des régressions d'ingénierie de release les plus courantes en acquisition d'utilisateurs mobiles se produit lorsque le deferred deep linking (handleDeepLink) et la restauration des paramètres d'installation (getInstallParam) d'une application fonctionnent de manière fiable lors des tests QA sur les builds de debug, mais renvoient des charges utiles (payloads) vides ou cessent de déclencher des callbacks une fois l'application compilée sous forme d'APK de release signé ou d'Android App Bundle (.aab).

La cause principale réside dans la disparité des types de build configurés dans Gradle. Par défaut, le type de build debug désactive la réduction du code pour accélérer les compilations incrémentales. Lorsque les ingénieurs compilent la variante release avec l'optimisation R8 activée, l'Android Gradle Plugin invoque le compilateur Android R8 pour exécuter trois transformations de bytecode à la fois sur l'application hôte et sur les bibliothèques tierces liées :

  • Réduction du code (Tree Shaking) : R8 analyse le graphe d'appels de l'application à partir des points d'entrée déclarés (tels que les composants AndroidManifest.xml et les directives -keep explicites) et supprime toute classe, constructeur, méthode ou champ qui semble inaccessible via une analyse statique.
  • Optimisation du bytecode et modification des accès : R8 intègre les méthodes en ligne (inlining), supprime les paramètres inutilisés, fusionne les classes et — lorsqu'il est associé à la directive -allowaccessmodification dans la configuration d'optimisation par défaut d'Android — modifie les indicateurs de visibilité des classes et des membres (comme l'élargissement des membres private ou package-private en public lors de l'inlining) afin de réduire la taille de l'exécutable Dalvik (.dex) et d'améliorer la vitesse d'exécution.
  • Obfuscation des identifiants (renommage et reconditionnement) : R8 renomme les classes, packages, champs et méthodes restants en identifiants compacts (tels que a.a.b()) et peut reconditionner les classes obfusquées dans des hiérarchies de packages aplaties, réduisant ainsi la surcharge du pool de chaînes à l'intérieur de classes.dex.

Le mode complet (Full Mode) de R8 est activé par défaut depuis l'Android Gradle Plugin (AGP) 8.0. Par rapport à l'ancien mode de compatibilité ProGuard, le mode complet de R8 applique des optimisations nettement plus strictes : il supprime les constructeurs par défaut sans argument (<init>()), modifie les modificateurs d'accès des membres et élimine les attributs de métadonnées, à moins que les classes et membres associés ne soient explicitement conservés par une règle de conservation correspondante. Pour un contexte fondamental sur les structures binaires .aar et les normes de sélection de SDK, consultez nos guides complémentaires : Comprendre la signification des SDK mobiles et les protocoles techniques de croissance et Comment choisir un kit de développement logiciel pour une attribution précise.

Six modes de défaillance distincts dans les SDK d'attribution obfusqués

Les callbacks par réflexion, les modèles et le JNI du SDK Android nécessitent des règles de préservation R8 ciblées.

Étant donné que l'analyse statique du graphe d'appels ne peut pas prédire les recherches dynamiques à l'exécution, un SDK Android non protégé peut rencontrer six modes de défaillance distincts sous le mode complet de R8. Les ingénieurs doivent distinguer ces modes avec soin, car chacun nécessite une cible de protection ProGuard spécifique :

Mode de défaillance à l'exécution Cause sous-jacente dans le bytecode R8 Cible de protection spécifique requise
1. Échec de recherche de classe par réflexion (ClassNotFoundException) R8 renomme, reconditionne ou supprime une classe chargée dynamiquement par son nom sous forme de chaîne via Class.forName() Préserver la classe cible et son identifiant d'origine à l'aide de -keep class ...
2. Échec d'instanciation d'objet par réflexion (NoSuchMethodException: <init>) En mode complet R8, même si une classe est conservée, son constructeur par défaut sans argument (<init>()) est supprimé s'il n'est pas directement appelé dans le code Préserver explicitement la signature du constructeur (<init>();) à l'intérieur du bloc -keep de la classe
3. Échecs de callbacks dynamiques et de modification d'accès R8 supprime ou renomme les méthodes d'interface de callback, ou modifie les indicateurs de visibilité des membres attendus par les appels réflectifs getDeclaredMethod() / getDeclaredField() Préserver les méthodes de l'interface de callback (-keep interface ... { <methods>; }) et tous les membres accessibles par réflexion
4. Perte des métadonnées de signature générique dans les écouteurs de callback Les types génériques Java subissent un effacement de type à la compilation, tandis que les attributs Signature du bytecode préservent les métadonnées génériques pour la réflexion à l'exécution (ResultCallBack<OpoData>). Le mode complet R8 supprime les métadonnées Signature sauf si elles sont explicitement retenues Conserver -keepattributes Signature en plus de conserver à la fois l'interface de callback générique et la classe du modèle cible
5. Échec de désérialisation du modèle de données JSON R8 supprime la classe du modèle (si elle n'est instanciée que par réflexion) ou renomme ses champs en a, b, c, ce qui amène les analyseurs JSON basés sur la réflexion à renvoyer des champs nuls ou vides Utiliser -keep class ... si le modèle est instancié dynamiquement, ou -keepclassmembers si la classe est déjà retenue via des références directes
6. Suppression des appels descendants JNI et des appels montants Natif-vers-Java (UnsatisfiedLinkError / NoSuchMethodError) Les règles Android par défaut préservent les noms sur les appels descendants Java native survivants, mais n'empêchent pas la suppression des méthodes RegisterNatives inutilisées et ne peuvent pas voir le code C/C++ .so rappelant Java via JNI (FindClass, GetMethodID) Utiliser explicitement -keep sur les déclarations native enregistrées dynamiquement ainsi que sur les classes, constructeurs et méthodes Java invoqués via des appels montants JNI depuis C/C++
[Android Release Build: R8 Full Mode Enabled]
                      |
                      v
     [R8 Evaluates Bundled & Host Keep Rules]
                      |
      +---------------+---------------+
      |                               |
      v (Missing / Mismatched Rules)  v (Scoped Keep Rules + Attributes Active)
[SDK Symbols Stripped or Renamed] [Required SDK Entry Points & Models Preserved]
- Reflective () Removed     - Confirmed in -printseeds (seeds.txt)
- JNI Upcall / Model Fields Lost  - Verified in Raw DEX (apkanalyzer dex packages)
- Generic Signature Metadata Lost - Release Attribution Matches Debug Behavior

Règles consommateur dans les bundles AAR vs importations manuelles de JAR

Comment les bibliothèques AAR et JAR intègrent les règles ProGuard consommateur

Les archives Android AAR et JAR fournissent des règles consommateur à la configuration R8.

Avant d'ajouter manuellement des règles d'exclusion à votre application hôte, vérifiez si le SDK Android tiers intègre déjà des règles R8. Comme documenté dans le Guide des développeurs Google Android sur l'optimisation des bibliothèques, AGP et R8 prennent en charge la découverte automatique des règles consommateur dans les distributions .aar et .jar, y compris les règles R8 ciblant des versions spécifiques :

Format de distribution du SDK Emplacement des règles consommateur intégrées Comportement de compilation d'Android Gradle Plugin / R8
Archive Android (.aar) /proguard.txt à la racine de l'AAR (via consumerProguardFiles), ou règles ciblées par version dans /classes.jar sous /META-INF/com.android.tools/r8-from-<X>-upto-<Y>/ Extraites et fusionnées automatiquement dans la configuration R8 de l'application hôte lors de la compilation
Archive Java (.jar) avec règles intégrées /META-INF/proguard/*.pro ou /META-INF/com.android.tools/r8-from-<X>-upto-<Y>/ (incluant les variantes r8, r8-from-<X> et r8-upto-<Y>) Découvertes et appliquées automatiquement par AGP et R8 selon la version active du compilateur R8
.jar ou .aar autonome sans règles intégrées Aucune règle présente dans l'archive Aucune protection consommateur intégrée n'est fournie ; ajoutez des règles de conservation au niveau de l'hôte uniquement lorsque la réflexion à l'exécution, le JNI, la sérialisation ou d'autres symboles accédés dynamiquement nécessitent une préservation explicite

(Remarque sur les règles de sérialisation Gson : comme indiqué dans le Guide de dépannage Gson pour ProGuard et R8, les versions 2.11.0 et ultérieures de Gson intègrent des règles R8 pour TypeToken et les champs annotés avec @SerializedName. Ces règles n'éliminent pas la nécessité de restreindre l'accès réflectif aux modèles, de fournir des constructeurs sans argument compatibles ou de tester tous les chemins de sérialisation et de désérialisation dans les builds de release minifiés.)

Inspecter votre archive SDK téléchargée avant d'écrire des règles

La correspondance des règles ProGuard et R8 est strictement sensible à la casse et exige un chemin de package exact. Selon les versions du SDK ou les distributions régionales, les espaces de noms des packages peuvent varier — et si vous copiez une règle issue d'un ancien tutoriel dont les majuscules/minuscules ne correspondent pas aux fichiers .class réels de votre archive, R8 n'appliquera la règle à aucune classe, sans émettre d'alerte.

Avant de modifier vos règles de conservation, téléchargez votre package SDK cible (tel que le SDK Android v2.1.0 depuis le centre de téléchargement de SDK mobiles) et inspectez ses règles intégrées (à la fois dans /proguard.txt et à l'intérieur de classes.jar) ainsi que sa hiérarchie exacte de fichiers .class :

# 1. Inspect an .aar archive for root consumer rules (proguard.txt) and extract classes.jar to check META-INF rules
unzip -l opoinstall-android-sdk.aar
unzip -p opoinstall-android-sdk.aar proguard.txt
unzip -p opoinstall-android-sdk.aar classes.jar > temp_classes.jar
jar tf temp_classes.jar | grep -E "META-INF/proguard/|META-INF/com\.android\.tools/|\.class$"

# 2. If using a standalone .jar archive, inspect embedded META-INF rules and exact case-sensitive .class paths
jar tf libs/OpoInstall_SDK.jar | grep -E "META-INF/proguard/|META-INF/com\.android\.tools/|\.class$"

Configurer proguard-rules.pro pour les SDK Android d'attribution

Éviter les anti-patterns de conservation globale de packages et les suppressions génériques d'avertissements

Lors de la rédaction de règles personnalisées, les ingénieurs doivent suivre les bonnes pratiques de Google détaillées dans Choisir quoi conserver (Règles de conservation R8) afin d'éviter de désactiver inutilement l'optimisation :

  • Préférer des règles ciblées aux anti-patterns globaux -keep class package.** { *; } : Une règle globale à l'échelle d'un package force R8 à conserver et à ignorer l'optimisation sur chaque classe utilitaire interne de ce package. Limitez vos règles aux classes spécifiques, aux constructeurs (<init>();), aux interfaces de callback, aux cibles JNI et aux champs de modèles accédés dynamiquement à l'exécution (ou utilisez l'annotation @UnconditionallyKeep de Jetpack sur le code que vous contrôlez).
  • Distinguer -keep de -keepclassmembers sur les modèles de données : -keepclassmembers ne préserve les champs ou méthodes spécifiés que si la classe englobante elle-même est retenue dans le graphe d'appels. Si un modèle de données (tel que OpoData) est instancié uniquement par réflexion ou référencé uniquement via des types génériques effacés, utilisez -keep class ... afin que R8 ne supprime pas la classe elle-même.
  • Comprendre les limites de -keepclasseswithmembernames pour le JNI : Les règles d'optimisation par défaut d'Android incluent -keepclasseswithmembernames,includedescriptorclasses class * { native <methods>; }. Comme défini dans la documentation des règles de conservation R8 de Google et dans le Manuel ProGuard sur les directives Keep, -keepclasseswithmembernames équivaut à -keepclasseswithmembers,allowshrinking. Elle préserve les noms des classes et des membres correspondants qui survivent à la phase de réduction, mais n'empêche pas la suppression des classes ou des méthodes native non référencées lors de la réduction. Si des méthodes natives sont enregistrées dynamiquement via RegisterNatives sans points d'appel Java directs, ou si du code C/C++ invoque des méthodes Java via des appels montants JNI, vous devez utiliser des règles explicites -keep ou -keepclassmembers selon la manière dont la classe cible est instanciée.
  • Éviter les directives globales -dontwarn non vérifiées : -dontwarn n'offre aucune protection contre la réduction ou le renommage et masque les avertissements de références non résolues qui peuvent cacher des plantages NoClassDefFoundError à l'exécution. N'ajoutez un -dontwarn strictement ciblé que si votre journal de compilation signale une classe spécifique manquante, utilisée uniquement à la compilation, et dont vous avez vérifié le caractère facultatif à l'exécution.
Directive ProGuard / R8 Syntaxe longue équivalente Réduction (Suppression) Obfuscation (Renommage) Portée d'ingénierie recommandée
-keep class ... -keep Empêchée (Classe et membres spécifiés conservés) Empêchée (Noms d'origine conservés) Points d'entrée publics du SDK, interfaces de callback et classes instanciées dynamiquement via réflexion ou JNI (inclure <init>(...);)
-keepclassmembers class ... -keepclassmembers Membres conservés uniquement si la classe est retenue Empêchée pour les membres correspondants Préservation des <fields>; ou des méthodes d'appel montant JNI sur les classes déjà directement instanciées en Java/Kotlin
-keepclasseswithmembernames -keepclasseswithmembers,allowshrinking Autorisée si non référencée (allowshrinking) Empêchée si la classe et les membres survivent à la réduction Préservation par défaut des noms d'appels descendants JNI (native <methods>;) lorsque le code Java invoque directement la méthode native
-keepnames class ... -keep,allowshrinking Autorisée si non référencée (allowshrinking) Empêchée si la classe est retenue Conserver des noms de classes lisibles pour les traces de pile (stack traces) sans forcer l'inclusion de classes inutilisées dans classes.dex
-keepattributes ... -keepattributes Préserve les métadonnées sur les éléments conservés N/A (Attribut de bytecode) Conservation de Signature (génériques), InnerClasses, EnclosingMethod et *Annotation* sur les classes explicitement conservées
-dontwarn <specific_class> -dontwarn Aucun effet sur la réduction Aucun effet sur le renommage Suppression des avertissements de compilation uniquement pour une dépendance optionnelle vérifiée, utilisée seulement à la compilation

Configurer Gradle sur les chaînes d'outils AGP 8.x et AGP 9.3+

L'Android Gradle Plugin 9.3 a introduit un bloc DSL optimization simplifié ainsi que des répertoires keepRules spécifiques aux variantes (fichiers .keep), tandis que les versions AGP 8.x à 9.2 (et les configurations historiques toujours prises en charge dans AGP 9.3+) utilisent isMinifyEnabled et proguardFiles. Les ingénieurs doivent bien distinguer trois objets de configuration :

Objet de configuration Emplacement du fichier ou du DSL Rôle architectural dans les builds R8
Règles de conservation du DSL historique (AGP 8.x–9.2 & Historique 9.3+) app/proguard-rules.pro (référencé via proguardFiles(...)) Déclare les règles de conservation de l'hôte aux côtés de getDefaultProguardFile("proguard-android-optimize.txt")
Règles de conservation du nouveau DSL d'optimisation (AGP 9.3+) app/src/main/keepRules/*.keep ou app/src/<variant>/keepRules/*.keep Déclare les fichiers .keep lors de l'utilisation de optimization { enable = true } (les règles par défaut de la plateforme Android sont appliquées automatiquement ; le DSL historique prend également en charge les ensembles de sources .keep)
Directives de rapport de diagnostic R8 -printseeds, -printusage, -printconfiguration dans un fichier de configuration R8 vérifié (proguard-rules.pro) Demande à R8 d'émettre des rapports d'audit dans app/build/outputs/mapping/<buildVariant>/
// Option A: AGP 8.x - 9.2 Legacy DSL (also supported in AGP 9.3+) — app/build.gradle.kts
android {
    buildTypes {
        getByName("release") {
            isMinifyEnabled = true
            isShrinkResources = true
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
        }
    }
}

// Option B: AGP 9.3+ Simplified Optimization DSL — app/build.gradle.kts
// Place scoped class/member keep rules in app/src/main/keepRules/sdk-attribution.keep
android {
    buildTypes {
        release {
            optimization {
                enable = true
            }
        }
    }
}

Exemples de règles de conservation ciblées et de directives de diagnostic

Étant donné que la documentation publique d'OpoInstall et les anciennes pages de FAQ font référence à différents préfixes de packages (com.opoinstall.api.** contre com.fm.OpoInstall.** ou io.OpoInstall.**), l'ensemble de règles ci-dessous est fourni à titre d'exemple uniquement — il ne s'agit pas d'un ensemble de règles de production vérifié pour le SDK OpoInstall v2.1.0. Inspectez d'abord les chemins de classes réels et les règles consommateur intégrées dans votre archive v2.1.0 téléchargée, et consultez la documentation technique d'OpoInstall (voir Comment les kits de développement logiciel propulsent l'attribution et la croissance mobiles pour le code Java sous-jacent du cycle de vie) :

# File: app/src/main/keepRules/sdk-attribution.keep (or app/proguard-rules.pro)
# IMPORTANT: Illustrative only — not a verified production rule set for OpoInstall SDK v2.1.0.
# Verify exact case-sensitive package and class names inside your downloaded v2.1.0 archive
# via `jar tf` or `apkanalyzer`, and check `configuration.txt` for existing consumer rules.

# ---------------------------------------------------------------------------
# 1. Preserve Bytecode Attributes on Explicitly Kept Classes
# Required so generic callback interfaces (e.g., ResultCallBack<OpoData>)
# retain their parameterized type metadata in R8 Full Mode.
# ---------------------------------------------------------------------------
-keepattributes Signature, InnerClasses, EnclosingMethod, *Annotation*

# ---------------------------------------------------------------------------
# 2. Scoped SDK Entry Point, Callback Interface, and Data Model Rules
# ---------------------------------------------------------------------------

# Preserve public SDK entry point and any reflectively invoked constructors/methods:
-keep public class com.opoinstall.api.OpoInstall {
    <init>(...);
    public <methods>;
}

# Preserve callback listener interfaces and their callback methods:
-keep public interface com.opoinstall.api.ResultCallBack {
    public <methods>;
}

# Model Scenario A: If OpoData / OpoError models are instantiated dynamically via reflection
# or referenced only via erased generics, use -keep to preserve both the class and its members:
-keep class com.opoinstall.api.OpoData {
    <init>(...);
    <fields>;
    public <methods>;
}
-keep class com.opoinstall.api.OpoError {
    <init>(...);
    <fields>;
    public <methods>;
}

# Model Scenario B: If a model class is already directly retained in your host call graph
# and only its fields require protection from renaming, use -keepclassmembers:
# -keepclassmembers class com.opoinstall.api.OpoData { <fields>; }

# ---------------------------------------------------------------------------
# 3. JNI Downcall and Native-to-Java Upcall Protection (Illustrative JNI Patterns)
# ---------------------------------------------------------------------------
# JNI Case A: If Java/Kotlin code already directly instantiates the JNI bridge class,
# use -keepclassmembers,includedescriptorclasses for C/C++ upcall methods, and keep
# any parameter model classes/constructors instantiated by native code:
-keepclassmembers,includedescriptorclasses class com.example.sdk.internal.JniBridge {
    public void onNativeAttributionEvent(com.example.sdk.internal.NativeEventData);
}
-keep class com.example.sdk.internal.NativeEventData {
    <init>(...);
}

# JNI Case B: If native C/C++ code dynamically looks up or instantiates the Java class
# itself via FindClass / NewObject, or registers native methods via RegisterNatives
# without direct Java call sites, use -keep to prevent class and method removal:
-keep,includedescriptorclasses class com.example.sdk.internal.DynamicNativeBridge {
    <init>(...);
    public native <methods>;
    public void onNativeCallback(java.lang.String);
}
# File: app/proguard-rules.pro (Diagnostic Output Directives)
# Place R8 diagnostic report flags in your verified R8 configuration file.
# Paths resolve relative to the module working directory (`app/`).
# Adjust the directory name for product flavors (e.g., build/outputs/mapping/prodRelease/).
-printseeds build/outputs/mapping/release/seeds.txt
-printusage build/outputs/mapping/release/usage.txt
-printconfiguration build/outputs/mapping/release/configuration.txt

Résoudre les conflits d'obfuscation et de renforcement des packages par canal Android

Séparer la compilation R8, l'alignement de l'APK, la signature et les packages par canal

Pour les distributions Android en dehors de Google Play, OpoInstall prend en charge l'exportation de packages par canal Android dédiés — en prenant un APK de base téléversé par le développeur et en générant des APK spécifiques à chaque canal contenant des métadonnées de suivi channelCode distinctes.

Lors du dépannage de statistiques de canal manquantes ou d'échecs d'installation sur des packages par canal exportés, les ingénieurs doivent séparer deux critères de vérification de release indépendants : la validité de la signature cryptographique de l'APK et l'intégrité des métadonnées de canal du fournisseur :

  1. Réduction du code R8 à la compilation : R8 s'exécute au sein du pipeline de build Gradle lors de la compilation du bytecode Java/Kotlin en classes.dex avant que l'APK de base ne soit empaqueté et signé. R8 n'est jamais exécuté sur un APK déjà exporté.
  2. Alignement de l'APK Android (zipalign) et signature cryptographique (apksigner) : Les APK de release standards générés par AGP sont alignés automatiquement. Cependant, si votre pipeline de release applique un post-traitement personnalisé ou un renforcement (hardening) d'APK tiers avant la signature, vous devez réaligner l'APK conformément aux spécifications officielles Android zipalign et aux spécifications Android apksigner. zipalign aligne les données non compressées sur des limites de 4 octets — et si l'APK contient des bibliothèques natives .so non compressées, les équipes doivent utiliser zipalign -P 16 -f -v 4 infile.apk outfile.apk pour satisfaire aux exigences d'alignement de page de 16 Ko et vérifier l'alignement avec zipalign -c -P 16 -v 4. zipalign doit être exécuté avant la signature avec apksigner, car la modification d'un APK après sa signature invalide sa signature cryptographique globale V2/V3.
  3. Métadonnées de canal spécifiques au fournisseur et vérification de la signature : L'exécution d'outils tiers de renforcement (packing) ou de reconditionnement d'APK après l'exportation d'un APK de canal depuis la console OpoInstall peut écraser les métadonnées de canal intégrées ou invalider la signature de l'APK. Si un analyseur de package de canal tiers nécessite un schéma de signature d'APK spécifique (tel que l'activation de la signature JAR V1 aux côtés de la signature globale d'APK V2/V3), vérifiez directement cette exigence auprès de la documentation actuelle de votre console OpoInstall ou du support fournisseur avant de modifier les configurations de signature en production, et ne considérez pas la signature V1 comme une exigence universelle du système d'exploitation Android.

L'ordre recommandé du pipeline de release pour les packages par canal Android

Pour maintenir des signatures d'APK valides, l'optimisation du bytecode R8 et des métadonnées de canal fournisseur intactes, exécutez votre pipeline de release dans cet ordre strict :

  1. Compiler et minifier l'APK de base avec R8 : Exécutez ./gradlew :app:assembleRelease avec vos règles de conservation vérifiées afin que R8 optimise classes.dex au moment de la compilation.
  2. Effectuer tout renforcement, alignement et signature avant le téléversement : Si votre équipe de sécurité utilise des outils tiers de renforcement d'APK, appliquez-les à l'APK de base, exécutez zipalign -P 16 -f -v 4 (si un réalignement est requis avant la signature), vérifiez l'alignement avec zipalign -c -P 16 -v 4, puis signez l'APK de base.
  3. Téléverser l'APK de base et vérifier indépendamment les APK de canal exportés : Téléversez l'APK de base signé et obfusqué sur la console OpoInstall, exportez les APK de canal, et vérifiez indépendamment à la fois la validité de la signature cryptographique (apksigner verify --verbose) et l'attribution de canal sur un appareil physique avec l'APK exporté avant toute distribution publique.
[Recommended Channel Package Pipeline]:
Source Code -> Gradle R8 Minification -> (Optional Pre-Upload Hardening + zipalign -P 16 + Signing) -> Upload Base APK to Console -> Export Channel APK -> Verify Both Signature (apksigner) & Channel Callback

Auditer les symboles SDK préservés à l'aide des rapports R8 et d'APK Analyzer

Utiliser l'analyseur de configuration R8 autonome dans AGP 9.3+

Dans l'Android Gradle Plugin 9.3 et les versions ultérieures, vous n'avez pas besoin d'attendre la compilation complète d'un APK de release ou d'un App Bundle pour auditer la manière dont vos règles de conservation fusionnent et s'appliquent. AGP 9.3+ fournit une tâche Gradle dédiée analyze<Variant>R8Config (par exemple, ./gradlew :app:analyzeReleaseR8Config), qui évalue rapidement les règles de conservation consommateur et hôte et génère des diagnostics de configuration afin que les ingénieurs puissent itérer sur les règles .keep avant de lancer un build de release complet.

Vérification croisée de configuration.txt, seeds.txt, usage.txt et mapping.txt

Les rapports Android R8 et l'inspection DEX brute vérifient l'intégrité de la release de l'APK de canal.

Ne supposez jamais qu'une règle -keep a fonctionné simplement parce que ./gradlew :app:assembleRelease s'est terminé sans erreur de compilation. Pour vérifier votre configuration de manière déterministe, inspectez les quatre fichiers de diagnostic R8 générés dans app/build/outputs/mapping/<buildVariant>/ (utilisez -whyareyoukeeping class <ClassName> pour déboguer la raison pour laquelle un symbole spécifique a été retenu) :

  • configuration.txt : Contient l'ensemble complet des règles fusionnées appliquées par R8 — combinant les règles de conservation de votre hôte, les règles par défaut de la plateforme Android, les règles AAPT2 et toutes les règles /proguard.txt ou /META-INF/ intégrées dans les bibliothèques .aar et .jar liées.
  • seeds.txt : Liste chaque classe, constructeur (<init>()), méthode et champ correspondant à vos règles -keep actives. Notez que la présence dans seeds.txt confirme la syntaxe de la règle, mais doit être associée à une inspection DEX et à des tests à l'exécution.
  • usage.txt : Liste le code mort que R8 a supprimé de l'APK lors du tree-shaking. Confirmez qu'aucune des classes requises de votre SDK, constructeurs réflectifs ou méthodes d'appel montant JNI n'apparaît dans usage.txt.
  • mapping.txt : Enregistre la table de correspondance des identifiants avant et après renommage pour les classes et membres retenus. Notez que l'absence d'une classe dans mapping.txt ne prouve pas qu'elle a été préservée sous son nom d'origine — elle peut avoir été complètement supprimée lors de la réduction (et listée dans usage.txt). Effectuez toujours une vérification croisée de mapping.txt avec seeds.txt, usage.txt et le fichier DEX compilé.

Vérifier les identifiants DEX bruts par rapport aux états des rapports désobfusqués avec apkanalyzer

Lorsque vous utilisez l'outil officiel en ligne de commande APK Analyzer de Google Android Developers pour inspecter votre APK de release, faites la distinction entre les identifiants DEX compilés bruts et les noms désobfusqués via le fichier de mapping :

  • Inspection des noms DEX compilés bruts (sans --proguard-mappings) : Lorsque vous passez l'argument --proguard-mappings mapping.txt à apkanalyzer, l'outil reconvertit les noms DEX obfusqués (tels que a.b.c) en leurs noms sources d'origine pour l'affichage. Par conséquent, voir un nom de classe d'origine lorsque --proguard-mappings est activé ne prouve pas que le symbole a échappé au renommage dans classes.dex. Exécutez d'abord apkanalyzer dex packages sans --proguard-mappings. Comme le reconditionnement de classes de R8 peut également déplacer des classes dans des chemins de packages aplatis, ne vous fiez pas uniquement à un grep sur le nom du package ; inspectez l'arborescence brute des classes DEX, vérifiez les noms des classes de points d'entrée appelées par réflexion, contrôlez les noms des méthodes JNI et les signatures de descripteurs, et recoupez avec mapping.txt pour confirmer que les méthodes et champs requis n'ont pas été renommés.
  • Inspection des états de rétention R8 (avec --proguard-mappings, --proguard-seeds et --proguard-usages) : Lors d'une seconde passe, fournissez les trois fichiers de rapport R8 pour reconstruire les indicateurs de rétention (k pour conservé par une règle seed, x pour supprimé lors de la réduction, r pour référencé et d pour défini) sur chaque package (P), classe (C), méthode (M) et champ (F) :
# Run from the Android project root directory (adjust variant path for product flavors, e.g., prodRelease)

# 1. (AGP 9.3+) Run the standalone R8 configuration analyzer for rapid rule verification, then build release APK
./gradlew :app:analyzeReleaseR8Config
./gradlew :app:assembleRelease

# 2. Inspect the merged R8 configuration, matched seeds, and stripped usage reports
grep -i -C 3 "opoinstall" app/build/outputs/mapping/release/configuration.txt
grep -i "opoinstall" app/build/outputs/mapping/release/seeds.txt
grep -i "opoinstall" app/build/outputs/mapping/release/usage.txt

# 3A. Inspect RAW compiled DEX identifiers (WITHOUT --proguard-mappings)
# Also inspect the full raw DEX tree and cross-check mapping.txt in case R8 repackaged classes
apkanalyzer dex packages app/build/outputs/apk/release/app-release.apk | grep -i "opoinstall"

# 3B. Inspect R8 retention state flags (k = kept, x = removed, r = referenced, d = defined) WITH R8 report files
apkanalyzer dex packages \
  --proguard-mappings app/build/outputs/mapping/release/mapping.txt \
  --proguard-seeds app/build/outputs/mapping/release/seeds.txt \
  --proguard-usages app/build/outputs/mapping/release/usage.txt \
  app/build/outputs/apk/release/app-release.apk | grep -i "opoinstall"

# 4. Verify 16 KB page alignment (if native .so files are present) and cryptographic APK signatures
zipalign -c -P 16 -v 4 app/build/outputs/apk/release/app-release.apk
apksigner verify --verbose app/build/outputs/apk/release/app-release.apk

Matrice de dépannage de l'attribution sur les builds de release

Diagnostiquer les échecs silencieux d'attribution et de deep linking dans les builds Android minifiés

Lors du dépannage d'écarts d'attribution qui ne se produisent que dans les builds de release minifiés, utilisez la matrice de diagnostic ci-dessous pour isoler la cause principale :

Symptôme observé en release Cause principale probable liée à R8 / au build Étape de vérification diagnostique Action corrective
Le callback getInstallParam ou handleDeepLink ne se déclenche jamais en build de release Les classes de point d'entrée du SDK, les constructeurs réflectifs <init>() ou les méthodes ResultCallBack ont été supprimés ou renommés en raison d'une non-correspondance de package Vérifier seeds.txt et usage.txt ; inspecter la sortie brute de apkanalyzer dex packages (sans --proguard-mappings) et mapping.txt Inspecter le .jar / .aar avec jar tf pour trouver les chemins de classes exacts sensibles à la casse et ajouter des règles -keep ciblées incluant <init>(...);
Le callback se déclenche, mais les paramètres personnalisés OpoData sont analysés comme vides ou nuls OpoData n'était protégé qu'avec -keepclassmembers (permettant la suppression de la classe elle-même) ou les métadonnées génériques Signature ont été perdues Vérifier usage.txt et mapping.txt pour OpoData et vérifier la présence de -keepattributes Signature dans configuration.txt Utiliser -keep class ... (si OpoData est instancié par réflexion) avec <init>(...); <fields>; et conserver -keepattributes Signature
UnsatisfiedLinkError ou callback natif manquant lorsque le SDK charge la bibliothèque .so Les appels descendants RegisterNatives enregistrés dynamiquement ont été réduits ou les méthodes cibles d'appel montant C/C++ vers Java ont été renommées par R8 Inspecter logcat pour trouver le symbole JNI exact manquant et vérifier la sortie brute de apkanalyzer dex packages ainsi que mapping.txt pour la classe Java cible Utiliser -keep (ou -keepclassmembers si la classe de pont est directement instanciée en Java) avec includedescriptorclasses sur les cibles d'appel montant JNI et RegisterNatives
Le package par canal Android exporté échoue à l'installation ou perd les statistiques de canal Une modification de l'APK après exportation a invalidé la signature de l'APK ou écrasé les métadonnées de canal intégrées Exécuter zipalign -c -P 16 -v 4 et apksigner verify --verbose sur l'APK de canal exporté et tester sur un appareil physique Effectuer toute la minification R8, le renforcement, l'alignement et la signature sur l'APK de base avant de le téléverser sur la console ; vérifier à la fois la signature et l'attribution après l'exportation

Foire aux questions (FAQ)

Comment exclure un SDK mobile de l'obfuscation Android ProGuard et R8 ?
Tout d'abord, inspectez votre archive `.aar` (`/proguard.txt` et `classes.jar/META-INF/com.android.tools/`) ou `.jar` (`/META-INF/proguard/` et `/META-INF/com.android.tools/`) et consultez `configuration.txt` pour voir si des règles ProGuard consommateur sont déjà intégrées. Si des règles manuelles sont requises, ajoutez des directives `-keep` et `-keepclassmembers` strictement ciblées sur les classes d'entrée exactes du SDK (sensibles à la casse), les constructeurs réflectifs (`<init>(...);`), les interfaces de callback, les cibles JNI et les `<fields>;` des modèles, associées à `-keepattributes Signature, InnerClasses, EnclosingMethod, *Annotation*`.
Comment vérifier que les classes de mon SDK Android ont bien été exclues de l'obfuscation R8 ?
Activez `-printseeds`, `-printusage` et `-printconfiguration` dans votre configuration R8 (ou exécutez `./gradlew :app:analyzeReleaseR8Config` dans AGP 9.3+) et compilez votre APK de release. Vérifiez que les classes et constructeurs cibles de votre SDK apparaissent dans `seeds.txt` et ne figurent pas dans `usage.txt`. Ensuite, exécutez `apkanalyzer dex packages app-release.apk` **sans** `--proguard-mappings` et recoupez avec `mapping.txt` pour confirmer que les symboles DEX compilés bruts conservent leurs noms d'origine non obfusqués et leurs signatures de méthodes, puis exécutez `apkanalyzer` une seconde fois avec `--proguard-mappings`, `--proguard-seeds` et `--proguard-usages` pour auditer les indicateurs d'état de rétention (`k`, `x`, `r`, `d`).
Pourquoi les statistiques des packages par canal Android cessent-elles de fonctionner après le renforcement ou le post-traitement de l'APK ?
Lorsque vous exportez un package par canal Android depuis la console d'attribution, des métadonnées d'identification de canal sont intégrées dans l'APK exporté. Si vous exécutez par la suite des outils tiers de renforcement (hardening), de packing ou de re-signature sur cet APK de canal exporté, l'étape de post-traitement peut invalider la signature cryptographique de l'APK ou écraser les métadonnées de canal du fournisseur. Effectuez toujours la minification R8, le renforcement, `zipalign` et la signature sur votre APK de base *avant* de le téléverser sur la console, et vérifiez les APK de canal exportés avec `apksigner verify --verbose`.

Résumé et liste de contrôle de vérification du build de release

Comprendre comment exclure les SDK mobiles de l'obfuscation Android ProGuard et R8 est essentiel pour prévenir les régressions d'attribution silencieuses lors du passage des tests de debug aux builds de release en production. En vérifiant les règles consommateur intégrées, en rédigeant des directives -keep strictement ciblées qui correspondent aux chemins de classes exacts (sensibles à la casse) à l'intérieur de votre archive SDK, en respectant l'ordre de compilation préalable au téléversement pour les packages par canal Android, et en recoupant les fichiers de diagnostic R8 avec le bytecode DEX brut, les ingénieurs Android peuvent maintenir une réduction complète du code sans interrompre le deferred deep linking ni la restauration des paramètres.

Avant de publier un build de release Android minifié sur Google Play ou d'exporter des packages par canal, exécutez cette liste de contrôle de vérification en quatre étapes :

  • Inspecter l'archive SDK et la configuration fusionnée : Vérifiez votre archive SDK téléchargée (telle que la v2.1.0) avec jar tf ou unzip -l pour inspecter les chemins de classes exacts et les règles consommateur intégrées, puis exécutez ./gradlew :app:analyzeReleaseR8Config (dans AGP 9.3+) ou examinez configuration.txt.

  • Recouper les rapports R8 et les identifiants DEX bruts : Confirmez que les points d'entrée requis du SDK, les constructeurs réflectifs <init>(), les champs du modèle OpoData, les méthodes ResultCallBack et les signatures JNI apparaissent dans seeds.txt, sont absents de usage.txt et conservent leurs noms d'origine dans la sortie brute de apkanalyzer dex packages (exécuté sans --proguard-mappings) et dans mapping.txt.

  • Appliquer la minification avant le téléversement et vérifier les signatures des APK de canal : Terminez toute la minification R8, tout renforcement tiers, l'alignement zipalign (zipalign -c -P 16 -v 4) et la signature d'APK sur l'APK de base avant de le téléverser pour exporter des packages par canal Android, et exécutez apksigner verify --verbose sur les packages exportés.

  • Exécuter un test de validation (smoke test) de release sur appareil physique : Installez l'APK de release signé et minifié sur un appareil Android réel et vérifiez à la fois l'installation avec paramètres H5 (getInstallParam) et l'ouverture directe par lien (handleDeepLink).

Les équipes d'ingénierie peuvent enregistrer une application et téléverser leur APK de base obfusqué sur la console développeur OpoInstall pour tester l'exportation de packages par canal et vérifier la restauration des paramètres sur un build de release.

Ressources associées

Share this article