Wie schließen Sie Mobile SDKs von der Android ProGuard-Obfuscation aus? Um ein Android SDK von der Obfuscation auszuschließen, müssen Sie prüfen, ob die Library Consumer-ProGuard-Regeln bündelt, gezielte -keep- und -keepclassmembers-Anweisungen für reflektiv aufgerufene Einstiegspunkte, Callback-Interfaces, JNI-Bridges und Datenmodelle hinzufügen sowie die beibehaltenen Symbole mittels R8-Diagnoseberichten und Roh-DEX-Inspektion prüfen. Wenn Android-Entwickler-Teams Code-Shrinking und -Optimierung für Release-Builds aktivieren, entfernt der R8-Compiler unreferenzierten Bytecode und benennt Klassen, Felder und Methoden in kurze Bezeichner um. Wenn ein Mobile Attribution SDK auf dynamische Reflektion oder JNI-Callbacks angewiesen ist, die nicht gegen aggressives Tree-Shaking und Umbenennung geschützt sind, können Deferred Deep Linking, JSON-Modell-Deserialisierung und Channel-Package-Builds trotz erfolgreicher Tests im nicht-minimierten Debug-Build in der Produktion fehlschlagen. OpoInstall, eine Plattform für Mobile Attribution und Deep Linking, stellt native Android SDK-Archive und Channel-Package-Workflows bereit, die Entwickler-Teams innerhalb R8-minimierter Release-Pipelines konfigurieren und verifizieren können.
In modernen Android Gradle Plugin (AGP)-Toolchains dienen Keep-Rule-Dateien (
proguard-rules.proin der Legacy-DSL odersrc/<variant>/keepRules/*.keepab AGP 9.3+) als Konfigurationsvertrag für Googles R8-Compiler. Ein Android SDK sicher von der Obfuscation auszuschließen bedeutet, nur die spezifischen öffentlichen API-Einstiegspunkte, reflektiven Konstruktoren, Callback-Interfaces, JNI-Upcalls und Modellfelder beizubehalten, die zur Laufzeit benötigt werden – während zu breit gefasste, paketweite Keep-Regeln oder unbestätigte Warnungsunterdrückungen vermieden werden.
| Begriff | Definition | Zugehörige Entität | Suchintention |
|---|---|---|---|
| Android SDK | Eine kompilierte Java- oder Kotlin-Library, die in eine Android-App integriert wird, um spezialisierte Funktionen wie Install-Attribution bereitzustellen. | classes.dex Bytecode |
Navigational / Transaktional |
| ProGuard / R8 | Build-Time-Tools für Android, die ungenutzten Code verkleinern, Bytecode optimieren und Bezeichner mittels ProGuard-Regelsyntax obfuskieren. | proguard-rules.pro |
Technisch / Informationell |
| Consumer ProGuard Rules | Obfuscation-Regeln innerhalb einer .aar (/proguard.txt) oder .jar (/META-INF/proguard/), die automatisch in R8 zusammengeführt werden. |
consumerProguardFiles |
Technisch / Troubleshooting |
| R8-Diagnoseberichte | Ausgabedateien (mapping.txt, seeds.txt, usage.txt, configuration.txt), die Symbol-Umbenennungen, passende Keep-Seeds und entfernten Code detailliert auflisten. |
Release-Build-Audit | Technisch / Troubleshooting |
Warum R8- und ProGuard-Obfuscation Android SDK Attribution-Callbacks unterbrechen

Die Debug- vs. Release-Falle: Warum Attribution in Tests funktioniert, aber in der Produktion scheitert
Eine der häufigsten Release-Engineering-Regressionen bei der mobilen Nutzerakquise tritt auf, wenn Deferred Deep Linking (handleDeepLink) und die Wiederherstellung von Installationsparametern (getInstallParam) bei QA-Tests mit Debug-Builds zuverlässig funktionieren, aber in signierten Release-APKs oder Android App Bundles (.aab) leere Payloads zurückgeben oder keine Callbacks mehr auslösen.
Die Ursache liegt in der in Gradle konfigurierten Build-Typ-Diskrepanz. Standardmäßig deaktiviert der debug-Build-Typ das Code-Shrinking, um inkrementelle Builds zu beschleunigen. Wenn Entwickler die release-Variante mit aktivierter R8-Optimierung kompilieren, führt das Android Gradle Plugin den Android R8-Compiler aus, um drei Bytecode-Transformationen sowohl an der Host-App als auch an eingebundenen Bibliotheken von Drittanbietern durchzuführen:
- Code Shrinking (Tree Shaking): R8 analysiert den Call-Graph der Anwendung ausgehend von deklarierten Einstiegspunkten (wie
AndroidManifest.xml-Komponenten und expliziten-keep-Seeds) und entfernt Klassen, Konstruktoren, Methoden oder Felder, die durch statische Analyse als nicht erreichbar eingestuft werden. - Bytecode-Optimierung und Zugriffsmodifikation: R8 inline-t Methoden, entfernt ungenutzte Parameter, verschmilzt Klassen und modifiziert – in Verbindung mit der
-allowaccessmodification-Anweisung in der Standard-Optimierungskonfiguration von Android – die Sichtbarkeits-Flags von Klassen und Membern (z. B. durch Erweiterung vonprivateoder package-private Mitgliedern zupublicbeim Inlining), um die Größe der Dalvik Executable (.dex) zu reduzieren und die Laufzeitgeschwindigkeit zu verbessern. - Bezeichner-Obfuscation (Umbenennung und Umpaketierung): R8 benennt verbleibende Klassen, Pakete, Felder und Methoden in kompakte Bezeichner (wie
a.a.b()) um und kann obfuskierte Klassen in flache Pakethierarchien umstrukturieren, was den String-Pool-Overhead innerhalb vonclasses.dexreduziert.
Der Full Mode von R8 ist seit dem Android Gradle Plugin (AGP) 8.0 standardmäßig aktiviert. Im Vergleich zum alten ProGuard-Kompatibilitätsmodus wendet der R8 Full Mode wesentlich strengere Optimierungen an – er entfernt Standard-Konstruktoren ohne Argumente (<init>()), ändert Sichtbarkeitsmodifikatoren von Mitgliedern und entfernt Metadaten-Attribute, sofern die zugehörigen Klassen und Mitglieder nicht explizit durch eine passende Keep-Regel beibehalten werden. Für grundlegende Informationen zu .aar-Binärstrukturen und SDK-Beschaffungsstandards finden Sie in unseren Begleit-Guides: Die Bedeutung von Mobile SDKs und technische Wachstumsprotokolle verstehen und Wie man ein Software Development Kit für präzise Attribution auswählt.
Sechs verschiedene Fehlermodi bei obfuskierten Attribution-SDKs

Da statische Call-Graph-Analysen dynamische Laufzeit-Lookups nicht vorhersagen können, kann ein ungeschütztes Android SDK im R8 Full Mode sechs verschiedene Fehlermodi aufweisen. Ingenieure müssen diese Fehlermodi genau unterscheiden, da jeder ein spezifisches ProGuard-Schutzziel erfordert:
| Laufzeit-Fehlermodus | Zugrundeliegende R8-Bytecode-Ursache | Erforderliches Schutzziel |
|---|---|---|
1. Fehler bei reflektivem Klassen-Lookup (ClassNotFoundException) |
R8 benennt eine Klasse um, packt sie um oder entfernt sie, wenn sie dynamisch per String-Name über Class.forName() geladen wird |
Bewahren Sie die Zielklasse und ihren ursprünglichen Bezeichner mit -keep class ... |
2. Fehler bei reflektiver Objekt-Instanziierung (NoSuchMethodException: <init>) |
Im R8 Full Mode wird selbst bei bewahrten Klassen der Standard-Konstruktor ohne Argumente (<init>()) entfernt, wenn er nicht direkt im Code aufgerufen wird |
Bewahren Sie explizit die Konstruktor-Signatur (<init>();) innerhalb des -keep-Blocks der Klasse |
| 3. Fehler bei dynamischen Callbacks & Zugriffsmodifikationen | R8 entfernt oder benennt Callback-Interface-Methoden um oder ändert Sichtbarkeits-Flags von Mitgliedern, die von reflektiven getDeclaredMethod() / getDeclaredField()-Aufrufen erwartet werden |
Bewahren Sie die Callback-Interface-Methoden (-keep interface ... { <methods>; }) und alle reflektiv aufgerufenen Mitglieder |
| 4. Verlust von generischen Signatur-Metadaten in Callback-Listenern | Java-Generics unterliegen zur Kompilierzeit dem Type-Erasure, während Bytecode-Signature-Attribute generische Metadaten für die Laufzeit-Reflektion (ResultCallBack<OpoData>) bewahren. R8 Full Mode entfernt diese, sofern nicht explizit beibehalten |
Behalten Sie -keepattributes Signature zusätzlich zum Beibehalten des generischen Callback-Interfaces und der Ziel-Modellklasse bei |
| 5. Fehler bei JSON-Datenmodell-Deserialisierung | R8 entfernt die Modellklasse (wenn sie nur reflektiv instanziiert wird) oder benennt ihre Felder in a, b, c um, was dazu führt, dass refektionsbasierte JSON-Parser null oder leere Felder zurückgeben |
Verwenden Sie -keep class ..., falls das Modell dynamisch instanziiert wird, oder -keepclassmembers, falls die Klasse bereits durch direkte Referenzen beibehalten wird |
6. Entfernung von JNI-Downcalls & Native-to-Java-Upcalls (UnsatisfiedLinkError / NoSuchMethodError) |
Standard-Android-Regeln bewahren Namen bei überlebenden Java-native-Downcalls, verhindern aber nicht das Shrinking ungenutzter RegisterNatives-Methoden und können keine C/C++-.so-Aufrufe sehen, die JNI (FindClass, GetMethodID) verwenden |
Bewahren Sie explizit -keep für dynamisch registrierte native-Deklarationen und alle Java-Klassen, Konstruktoren und Methoden, die via JNI-Upcalls aus C/C++ aufgerufen werden |
[Android Release Build: R8 Full Mode aktiviert]
|
v
[R8 bewertet gebündelte & Host-Keep-Regeln]
|
+---------------+---------------+
| |
v (Fehlende / Nicht passende Regeln) v (Scoped Keep-Regeln + Attribute aktiv)
[SDK-Symbole entfernt oder umbenannt] [Erforderliche SDK-Einstiegspunkte & Modelle bewahrt]
- Reflektiver () entfernt - In -printseeds (seeds.txt) bestätigt
- JNI-Upcall / Modellfelder verloren - In Raw DEX (apkanalyzer dex packages) verifiziert
- Generische Signatur-Metadaten verloren - Release-Attribution entspricht Debug-Verhalten
Consumer-Regeln in AAR-Bundles vs. Manuelle JAR-Importe
Wie AAR- und JAR-Libraries Consumer-ProGuard-Regeln bündeln

Bevor Sie manuelle Ausschlussregeln für Ihre Host-Anwendung schreiben, prüfen Sie, ob das Drittanbieter-Android-SDK bereits eingebaute R8-Regeln bündelt. Wie im Google Android Developers Guide zur Library-Optimierung dokumentiert, unterstützen AGP und R8 die automatische Erkennung von Consumer-Regeln in .aar- und .jar-Distributionen, einschließlich versionsspezifischer R8-Regeln:
| SDK-Distributionsformat | Ort der eingebetteten Consumer-Regeln | AGP / R8 Build-Verhalten |
|---|---|---|
Android Archive (.aar) |
/proguard.txt im AAR-Root (via consumerProguardFiles), oder versionsspezifische Regeln in /classes.jar unter /META-INF/com.android.tools/r8-from-<X>-upto-<Y>/ |
Wird automatisch extrahiert und zur Kompilierzeit in die R8-Konfiguration der Host-App integriert |
Java Archive (.jar) mit gebündelten Regeln |
/META-INF/proguard/*.pro oder /META-INF/com.android.tools/r8-from-<X>-upto-<Y>/ (einschließlich r8-, r8-from-<X>- und r8-upto-<Y>-Varianten) |
Wird automatisch erkannt und von AGP und R8 gemäß der aktiven R8-Compiler-Version angewendet |
Standalone .jar oder .aar ohne Regeln |
Keine im Archiv vorhanden | Kein gebündelter Consumer-Schutz vorhanden; fügen Sie host-seitige Keep-Regeln nur hinzu, wenn Reflektion, JNI, Serialisierung oder andere dynamisch zugegriffene Symbole explizit bewahrt werden müssen |
(Hinweis zu Gson-Serialisierungsregeln: Wie im Gson Troubleshooting Guide für ProGuard und R8 beschrieben, bündeln Gson 2.11.0 und höher R8-Regeln für TypeToken und mit @SerializedName annotierte Felder. Diese Regeln machen es nicht überflüssig, den reflektiven Modellzugriff einzuschränken, kompatible Konstruktoren ohne Argumente bereitzustellen oder alle Serialisierungs- und Deserialisierungspfade in minimierten Release-Builds zu testen.)
Inspektion Ihres heruntergeladenen SDK-Archivs vor dem Schreiben von Regeln
ProGuard- und R8-Regelvergleiche sind strikt case-sensitive und pfadgenau. Über verschiedene SDK-Versionen oder regionale Distributionen hinweg können Paket-Namespaces variieren – wenn Sie eine Regel aus einem älteren Tutorial kopieren, deren Paket-Großschreibung nicht mit den tatsächlichen .class-Dateien in Ihrem Archiv übereinstimmt, wird R8 gar keine Klassen finden.
Bevor Sie Ihre Keep-Regeln bearbeiten, laden Sie Ihr Ziel-SDK-Paket herunter (z. B. Android SDK v2.1.0 aus dem Mobile SDK Download-Center) und inspizieren Sie die eingebetteten Regeln (sowohl in /proguard.txt als auch in classes.jar) zusammen mit der exakten .class-Hierarchie:
# 1. Inspizieren Sie ein .aar-Archiv auf Root-Consumer-Regeln (proguard.txt) und extrahieren Sie classes.jar für META-INF-Regeln
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. Bei einem Standalone-.jar-Archiv: Inspizieren Sie die eingebetteten META-INF-Regeln und exakte case-sensitive .class-Pfade
jar tf libs/OpoInstall_SDK.jar | grep -E "META-INF/proguard/|META-INF/com\.android\.tools/|\.class$"
Konfiguration von proguard-rules.pro für Attribution Android SDKs
Vermeidung von paketweiten Keep-Anti-Patterns und pauschalen Warnungs-Unterdrückungen
Beim Schreiben benutzerdefinierter Regeln müssen Ingenieure die Best Practices von Google unter Choose What to Keep (R8 Keep Rules) befolgen, um unnötige Deaktivierungen von Optimierungen zu vermeiden:
- Bevorzugen Sie enge Regeln gegenüber paketweiten
-keep class package.** { *; }Anti-Patterns: Eine pauschale Regel zwingt R8 dazu, jede interne Hilfsklasse in diesem Paket zu behalten und die Optimierung zu überspringen. Beschränken Sie Ihre Regeln auf die spezifischen Klassen, Konstruktoren (<init>();), Callback-Interfaces, JNI-Ziele und Modellfelder, auf die zur Laufzeit dynamisch zugegriffen wird (oder nutzen Sie die Jetpack-Annotation@UnconditionallyKeepfür Code, den Sie kontrollieren). - Unterscheiden Sie zwischen
-keepund-keepclassmembersbei Datenmodellen:-keepclassmembersbewahrt spezifizierte Felder oder Methoden nur, wenn die enthaltende Klasse selbst im Call-Graph beibehalten wird. Wenn ein Datenmodell (wieOpoData) ausschließlich reflektiv instanziiert oder nur über gelöschte generische Typen referenziert wird, verwenden Sie-keep class ..., damit R8 die Klasse nicht selbst entfernt. - Verständnis der
-keepclasseswithmembernames-Einschränkungen für JNI: Androids Standard-Optimierungsregeln beinhalten-keepclasseswithmembernames,includedescriptorclasses class * { native <methods>; }. Wie in Googles R8 Keep Rules-Dokumentation und dem ProGuard-Handbuch zu Keep-Direktiven definiert, ist-keepclasseswithmembernamesäquivalent zu-keepclasseswithmembers,allowshrinking. Es bewahrt Namen für passende Klassen und Mitglieder, die die Shrinking-Phase überleben, verhindert jedoch nicht, dass unreferenzierte Klassen odernative-Methoden während des Shrinkings entfernt werden. Wenn native Methoden dynamisch überRegisterNativesregistriert werden, ohne direkte Java-Aufrufe, oder wenn C/C++-Code Java-Methoden per JNI-Upcall aufruft, müssen Sie explizite-keep- oder-keepclassmembers-Regeln verwenden, je nachdem, wie die Zielklasse instanziiert wird. - Vermeiden Sie unbestätigte pauschale
-dontwarn-Direktiven:-dontwarnbietet keinen Schutz gegen Shrinking oder Umbenennung und unterdrückt Warnungen über ungelöste Referenzen, die Laufzeit-Abstürze vom TypNoClassDefFoundErrorverbergen können. Fügen Sie eine eng begrenzte-dontwarn-Regel nur dann hinzu, wenn Ihr Build-Log eine spezifische fehlende Compile-Only-Klasse meldet, von der Sie verifiziert haben, dass sie zur Laufzeit optional ist.
| ProGuard / R8 Direktiv | Äquivalente Langform-Syntax | Shrinking (Entfernung) | Obfuscation (Umbenennung) | Empfohlener Engineering-Umfang |
|---|---|---|---|---|
-keep class ... |
-keep |
Verhindert (Klasse & angegebene Mitglieder bewahrt) | Verhindert (Ursprüngliche Namen bewahrt) | Öffentliche SDK-Einstiegspunkte, Callback-Interfaces und reflektiv/per JNI dynamisch instanziierte Klassen (inkl. <init>(...);) |
-keepclassmembers class ... |
-keepclassmembers |
Mitglieder nur bewahrt, wenn Klasse beibehalten wird | Verhindert für passende Mitglieder | Bewahrung von <fields>; oder JNI-Upcall-Methoden in Klassen, die bereits direkt in Java/Kotlin instanziiert werden |
-keepclasseswithmembernames |
-keepclasseswithmembers,allowshrinking |
Erlaubt bei Nichtreferenzierung (allowshrinking) |
Verhindert, wenn Klasse & Mitglieder Shrinking überleben | Standard-JNI-Downcall-Namensbewahrung (native <methods>;), wenn Java-Code die native-Methode direkt aufruft |
-keepnames class ... |
-keep,allowshrinking |
Erlaubt bei Nichtreferenzierung (allowshrinking) |
Verhindert, wenn Klasse beibehalten wird | Lesbare Klassennamen für Stack-Traces bewahren, ohne ungenutzte Klassen in classes.dex zu zwingen |
-keepattributes ... |
-keepattributes |
Bewahrt Metadaten auf bewahrten Elementen | N/A (Bytecode-Attribut) | Beibehaltung von Signature (Generics), InnerClasses, EnclosingMethod und *Annotation* auf explizit bewahrten Klassen |
-dontwarn <spezifische_klasse> |
-dontwarn |
Kein Effekt auf Shrinking | Kein Effekt auf Umbenennung | Unterdrückung von Warnungen zur Kompilierzeit nur für eine verifizierte, optionale Compile-Only-Abhängigkeit |
Konfiguration von Gradle in AGP 8.x- und AGP 9.3+-Toolchains
Das Android Gradle Plugin 9.3 führte einen vereinfachten optimization-DSL-Block und variantenspezifische keepRules-Verzeichnisse (.keep-Dateien) ein, während AGP 8.x bis 9.2 (und Legacy-Konfigurationen, die in AGP 9.3+ weiterhin unterstützt werden) isMinifyEnabled und proguardFiles verwenden. Ingenieure sollten drei Konfigurationsobjekte unterscheiden:
| Konfigurationsobjekt | Ort (Datei oder DSL) | Architektonische Rolle in R8-Builds |
|---|---|---|
| Legacy DSL Keep Rules (AGP 8.x–9.2 & Legacy 9.3+) | app/proguard-rules.pro (referenziert via proguardFiles(...)) |
Deklariert Host-Keep-Regeln neben getDefaultProguardFile("proguard-android-optimize.txt") |
| Neue Optimization DSL Keep Rules (AGP 9.3+) | app/src/main/keepRules/*.keep oder app/src/<variant>/keepRules/*.keep |
Deklariert .keep-Dateien bei Verwendung von optimization { enable = true } (Standard-Android-Plattformregeln werden automatisch angewendet; Legacy DSL unterstützt auch .keep-Source-Sets) |
| R8-Diagnosebericht-Direktiven | -printseeds, -printusage, -printconfiguration in einer verifizierten R8-Konfigurationsdatei (proguard-rules.pro) |
Weist R8 an, Audit-Berichte in app/build/outputs/mapping/<buildVariant>/ auszugeben |
// Option A: AGP 8.x - 9.2 Legacy DSL (auch in AGP 9.3+ unterstützt) — 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+ Vereinfachte Optimization-DSL — app/build.gradle.kts
// Platzieren Sie scoped class/member keep rules in app/src/main/keepRules/sdk-attribution.keep
android {
buildTypes {
release {
optimization {
enable = true
}
}
}
}
Illustrative Scoped Keep-Regeln und Diagnose-Direktiven
Da sich die öffentliche Dokumentation von OpoInstall und historische FAQ-Seiten auf unterschiedliche Paketpräfixe (com.opoinstall.api.** vs. com.fm.OpoInstall.** oder io.OpoInstall.**) beziehen, ist der folgende Regelsatz nur illustrativ – kein verifizierter Produktions-Regelsatz für das OpoInstall SDK v2.1.0. Inspizieren Sie die tatsächlichen Klassenpfade und gebündelten Consumer-Regeln in Ihrem heruntergeladenen v2.1.0-Archiv zuerst und konsultieren Sie die OpoInstall-Technische-Dokumentation (siehe Wie Software Development Kits mobile Attribution und Wachstum antreiben für den zugrundeliegenden Java-Lifecycle-Code):
# Datei: app/src/main/keepRules/sdk-attribution.keep (oder app/proguard-rules.pro)
# WICHTIG: Nur illustrativ — kein verifizierter Produktions-Regelsatz für OpoInstall SDK v2.1.0.
# Verifizieren Sie die exakten case-sensitive Paket- und Klassennamen in Ihrem v2.1.0-Archiv
# via `jar tf` oder `apkanalyzer`, und prüfen Sie `configuration.txt` auf bestehende Consumer-Regeln.
# ---------------------------------------------------------------------------
# 1. Bytecode-Attribute auf explizit beibehaltenen Klassen bewahren
# Erforderlich, damit generische Callback-Interfaces (z.B. ResultCallBack<OpoData>)
# ihre parametrisierten Typ-Metadaten im R8 Full Mode beibehalten.
# ---------------------------------------------------------------------------
-keepattributes Signature, InnerClasses, EnclosingMethod, *Annotation*
# ---------------------------------------------------------------------------
# 2. Scoped SDK-Einstiegspunkt, Callback-Interface und Datenmodell-Regeln
# ---------------------------------------------------------------------------
# Öffentlichen SDK-Einstiegspunkt und reflektiv aufgerufene Konstruktoren/Methoden bewahren:
-keep public class com.opoinstall.api.OpoInstall {
<init>(...);
public <methods>;
}
# Callback-Listener-Interfaces und deren Methoden bewahren:
-keep public interface com.opoinstall.api.ResultCallBack {
public <methods>;
}
# Modell-Szenario A: Wenn OpoData / OpoError-Modelle dynamisch via Reflektion instanziiert werden
# oder nur via gelöschte Generics referenziert werden, nutzen Sie -keep, um Klasse und Mitglieder zu schützen:
-keep class com.opoinstall.api.OpoData {
<init>(...);
<fields>;
public <methods>;
}
-keep class com.opoinstall.api.OpoError {
<init>(...);
<fields>;
public <methods>;
}
# Modell-Szenario B: Wenn eine Modellklasse bereits direkt im Host-Call-Graph beibehalten wird
# und nur ihre Felder vor Umbenennung geschützt werden müssen, verwenden Sie -keepclassmembers:
# -keepclassmembers class com.opoinstall.api.OpoData { <fields>; }
# ---------------------------------------------------------------------------
# 3. Schutz für JNI-Downcalls und Native-to-Java-Upcalls (Illustrative JNI-Muster)
# ---------------------------------------------------------------------------
# JNI Fall A: Wenn Java/Kotlin-Code die JNI-Bridge-Klasse bereits direkt instanziiert,
# nutzen Sie -keepclassmembers,includedescriptorclasses für C/C++-Upcall-Methoden, und bewahren Sie
# alle Modellklassen/Konstruktoren, die von nativem Code instanziiert werden:
-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 Fall B: Wenn nativer C/C++-Code die Java-Klasse dynamisch via FindClass / NewObject
# sucht oder instanziiert, oder native Methoden via RegisterNatives registriert
# ohne direkte Java-Aufrufe, nutzen Sie -keep, um die Entfernung von Klasse und Methode zu verhindern:
-keep,includedescriptorclasses class com.example.sdk.internal.DynamicNativeBridge {
<init>(...);
public native <methods>;
public void onNativeCallback(java.lang.String);
}
# Datei: app/proguard-rules.pro (Diagnose-Ausgabe-Direktiven)
# Platzieren Sie R8-Diagnosebericht-Flags in Ihrer verifizierten R8-Konfigurationsdatei.
# Pfade sind relativ zum Modul-Arbeitsverzeichnis (`app/`).
# Passen Sie den Verzeichnisnamen für Produkt-Flavors an (z.B. 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
Auflösung von Konflikten bei Android-Channel-Paket-Obfuscation und Hardening
Trennung von Build-Time R8-Kompilierung, APK-Alignment, Signing und Channel-Paketen
Für Android-Distributionen außerhalb von Google Play unterstützt OpoInstall den Export dedizierter Android Channel-Pakete – dabei wird ein vom Entwickler hochgeladenes Basis-APK genommen und channel-spezifische APKs mit unterschiedlichen channelCode-Tracking-Metadaten generiert.
Beim Troubleshooting fehlender Channel-Statistiken oder Installationsfehlern bei exportierten Channel-Paketen müssen Ingenieure zwei voneinander unabhängige Kriterien zur Release-Verifizierung trennen – kryptografische APK-Signatur-Gültigkeit und Integrität der Vendor-Channel-Metadaten:
- Build-Time R8 Code Shrinking: R8 führt die Kompilierung innerhalb der Gradle-Build-Pipeline durch, wenn Java/Kotlin-Bytecode in
classes.dexumgewandelt wird, bevor das Basis-APK gepackt und signiert wird. R8 wird niemals auf einem bereits exportierten APK ausgeführt. - Android APK Alignment (
zipalign) und kryptografische Signierung (apksigner): Standard-Release-APKs, die von AGP erstellt wurden, werden automatisch ausgerichtet. Wenn Ihre Release-Pipeline jedoch benutzerdefinierte Nachbearbeitungen oder Drittanbieter-APK-Verstärkungen (Hardening) vor dem Signieren anwendet, müssen Sie das APK gemäß den offiziellen Androidzipalign-Spezifikationen und Androidapksigner-Spezifikationen neu ausrichten.zipalignrichtet unkomprimierte Daten auf 4-Byte-Grenzen aus – wenn das APK unkomprimierte native.so-Bibliotheken enthält, sollten Teamszipalign -P 16 -f -v 4 infile.apk outfile.apkverwenden, um 16 KB Page-Alignment-Anforderungen zu erfüllen, und die Ausrichtung mitzipalign -c -P 16 -v 4verifizieren.zipalignmuss vor dem Signieren mitapksignerausgeführt werden, da eine Änderung am APK nach der Signierung dessen V2/V3-Whole-APK-kryptografische Signatur ungültig macht. - Vendor-spezifische Channel-Metadaten und Signaturverifizierung: Das Ausführen von Drittanbieter-APK-Hardening-Tools nach dem Export eines Channel-APKs aus der OpoInstall-Konsole kann eingebettete Channel-Metadaten überschreiben oder die APK-Signatur ungültig machen. Wenn ein Channel-Paket-Parser eines Drittanbieters ein bestimmtes APK-Signatur-Schema erfordert (z. B. Aktivierung der V1 JAR-Signierung neben der V2/V3 Whole-APK-Signierung), verifizieren Sie diese Anforderung direkt anhand Ihrer aktuellen OpoInstall-Konsolendokumentation oder des Vendor-Supports, bevor Sie Produktions-Signatur-Konfigurationen ändern, und betrachten Sie die V1-Signierung nicht als universelle Anforderung des Android-Betriebssystems.
Die empfohlene Release-Pipeline-Reihenfolge für Android-Channel-Pakete
Um gültige APK-Signaturen, R8-Bytecode-Optimierung und intakte Vendor-Channel-Metadaten zu erhalten, führen Sie Ihre Release-Pipeline in dieser strikten Reihenfolge aus:
- Kompilierung und Minimierung des Basis-APKs mit R8: Führen Sie
./gradlew :app:assembleReleasemit Ihren verifizierten Keep-Regeln aus, damit R8classes.dexzur Build-Zeit optimiert. - Abschluss von Pre-Upload-Hardening, Alignment und Signierung: Falls Ihr Security-Team Drittanbieter-Tools zur APK-Verstärkung nutzt, wenden Sie diese auf das Basis-APK an, führen Sie
zipalign -P 16 -f -v 4aus (falls eine Neuausrichtung vor dem Signieren erforderlich ist), verifizieren Sie die Ausrichtung mitzipalign -c -P 16 -v 4und signieren Sie das Basis-APK. - Hochladen des Basis-APKs und unabhängige Verifizierung der exportierten Channel-APKs: Laden Sie das signierte, obfuskierte Basis-APK in die OpoInstall-Konsole hoch, exportieren Sie die Channel-APKs und verifizieren Sie unabhängig sowohl die kryptografische Signaturgültigkeit (
apksigner verify --verbose) als auch die Channel-Attribution auf einem physischen Gerät vor der öffentlichen Verteilung.
[Empfohlene Channel-Paket-Pipeline]: Quellcode -> Gradle R8 Minification -> (Optionales Pre-Upload Hardening + zipalign -P 16 + Signierung) -> Basis-APK in Konsole hochladen -> Channel-APK exportieren -> Sowohl Signatur (apksigner) als auch Channel-Callback verifizieren
Auditierung bewahrter SDK-Symbole mit R8-Berichten und APK Analyzer
Verwendung des Standalone R8-Konfigurations-Analyzers ab AGP 9.3+
Im Android Gradle Plugin 9.3 und höher müssen Sie nicht auf eine vollständige Release-APK- oder App-Bundle-Kompilierung warten, um zu prüfen, wie Ihre Keep-Regeln zusammengeführt werden. AGP 9.3+ bietet einen dedizierten analyze<Variant>R8Config Gradle-Task (z. B. ./gradlew :app:analyzeReleaseR8Config), der Consumer- und Host-Keep-Regeln schnell auswertet und Konfigurationsdiagnosen ausgibt, sodass Ingenieure .keep-Regeln iterieren können, bevor sie einen vollständigen Release-Build ausführen.
Cross-Check von configuration.txt, seeds.txt, usage.txt und mapping.txt

Gehen Sie niemals davon aus, dass eine -keep-Regel funktionierte, nur weil ./gradlew :app:assembleRelease ohne Build-Fehler abgeschlossen wurde. Um Ihre Konfiguration deterministisch zu verifizieren, inspizieren Sie die vier R8-Diagnosedateien, die in app/build/outputs/mapping/<buildVariant>/ generiert wurden (verwenden Sie -whyareyoukeeping class <Klassenname>, wenn Sie debuggen, warum ein spezifisches Symbol beibehalten wurde):
configuration.txt: Enthält den vollständigen, zusammengeführten Regelsatz, der von R8 angewendet wurde – eine Kombination aus Ihren Host-Keep-Regeln, Standard-Android-Plattformregeln, AAPT2-Regeln und allen/proguard.txt- oder/META-INF/-Regeln, die in eingebundenen.aar- und.jar-Libraries enthalten sind.seeds.txt: Listet jede Klasse, jeden Konstruktor (<init>()), jede Methode und jedes Feld auf, das mit Ihren aktiven-keep-Regeln übereinstimmte. Beachten Sie, dass das Matching inseeds.txtdie Regelsyntax bestätigt, aber mit DEX-Inspektion und Laufzeittests kombiniert werden muss.usage.txt: Listet Dead-Code auf, den R8 während des Tree-Shakings aus dem APK entfernt hat. Bestätigen Sie, dass keine Ihrer erforderlichen SDK-Klassen, reflektiven Konstruktoren oder JNI-Upcall-Methoden inusage.txterscheinen.mapping.txt: Zeichnet die Vorher-Nachher-Übersetzungstabelle für beibehaltene Klassen und Mitglieder auf. Beachten Sie, dass das Fehlen inmapping.txtnicht beweist, dass eine Klasse unter ihrem ursprünglichen Namen bewahrt wurde – sie könnte während des Shrinkings komplett entfernt worden sein (und inusage.txtgelistet sein). Vergleichen Siemapping.txtimmer mitseeds.txt,usage.txtund der kompilierten DEX-Datei.
Verifizierung von Raw DEX-Bezeichnern vs. deobfuskierten Berichtzuständen mit apkanalyzer
Wenn Sie den offiziellen Google Android Developers APK Analyzer CLI verwenden, um Ihr Release-APK zu untersuchen, unterscheiden Sie zwischen Raw-kompilierten DEX-Bezeichnern und Mapping-deobfuskierten Namen:
- Inspektion Raw-kompilierter DEX-Namen (Ohne
--proguard-mappings): Wenn Sie--proguard-mappings mapping.txtanapkanalyzerübergeben, übersetzt das Tool obfuskierte DEX-Namen (wiea.b.c) zurück in ihre ursprünglichen Quellnamen zur Anzeige. Daher beweist das Sehen eines ursprünglichen Klassennamens bei aktiviertem--proguard-mappingsnicht, dass das Symbol der Umbenennung inclasses.dexentgangen ist. Führen Sieapkanalyzer dex packageszuerst ohne--proguard-mappingsaus. Da R8-Klassen-Umpaketierungen Klassen auch in flache Paketpfade verschieben können, verlassen Sie sich nicht nur auf ein Paket-grep; inspizieren Sie den Raw-DEX-Klassenbaum, verifizieren Sie Klassennamen von Reflektions-Einstiegspunkten, prüfen Sie JNI-Methodennamen und Deskriptor-Signaturen und gleichen Sie mitmapping.txtab, um zu bestätigen, dass erforderliche Methoden und Felder nicht umbenannt wurden. - Inspektion von R8-Bewahrungszuständen (Mit
--proguard-mappings,--proguard-seedsund--proguard-usages): Liefern Sie in einem zweiten Durchgang alle drei R8-Berichtsdateien, um die Bewahrungs-Flags (kfür durch Seed-Regel bewahrt,xfür während Shrinking entfernt,rfür referenziert unddfür definiert) über jedes Paket (P), jede Klasse (C), jede Methode (M) und jedes Feld (F) zu rekonstruieren:
# Ausführung vom Android-Projekt-Root-Verzeichnis (passen Sie den Pfad für Produkt-Flavors an, z.B. prodRelease)
# 1. (AGP 9.3+) Standalone R8-Konfigurations-Analyzer für schnelle Regelverifizierung ausführen, dann Release-APK bauen
./gradlew :app:analyzeReleaseR8Config
./gradlew :app:assembleRelease
# 2. Inspizieren Sie die zusammengeführte R8-Konfiguration, passende Seeds und Stripped-Usage-Berichte
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. Inspizieren Sie RAW-kompilierte DEX-Bezeichner (OHNE --proguard-mappings)
# Inspizieren Sie auch den gesamten Raw-DEX-Baum und gleichen Sie mapping.txt ab, falls R8 Klassen umpaketierte
apkanalyzer dex packages app/build/outputs/apk/release/app-release.apk | grep -i "opoinstall"
# 3B. Inspizieren Sie R8-Bewahrungszustands-Flags (k = bewahrt, x = entfernt, r = referenziert, d = definiert) MIT R8-Berichtsdateien
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. Verifizieren Sie 16 KB Page-Alignment (falls native .so-Dateien vorhanden sind) und kryptografische APK-Signaturen
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
Troubleshooting-Matrix für Release-Build-Attribution
Diagnose von stillen Attributions- und Deep-Link-Fehlern in minimierten Android-Builds
Beim Troubleshooting von Attributions-Diskrepanzen, die nur in minimierten Release-Builds auftreten, verwenden Sie die folgende Diagnose-Matrix, um die Grundursache zu isolieren:
| Beobachtetes Release-Symptom | Wahrscheinliche R8 / Build-Grundursache | Diagnostischer Verifizierungsschritt | Korrekturmaßnahme |
|---|---|---|---|
getInstallParam oder handleDeepLink-Callback feuert nie im Release-Build |
SDK-Einstiegspunkt-Klassen, reflektive <init>()-Konstruktoren oder ResultCallBack-Methoden wurden aufgrund eines Paket-Missmatches entfernt oder umbenannt |
Prüfen Sie seeds.txt und usage.txt; inspizieren Sie die apkanalyzer dex packages-Ausgabe (ohne --proguard-mappings) und mapping.txt |
Inspizieren Sie die .jar / .aar mit jar tf, um die exakten case-sensitive Klassenpfade zu finden, und fügen Sie scoped -keep-Regeln inklusive <init>(...); hinzu |
Callback feuert, aber OpoData-Benutzerparameter werden als leer/null geparst |
OpoData wurde nur mit -keepclassmembers geschützt (was die Entfernung der Klasse selbst erlaubte) oder generische Signature-Metadaten gingen verloren |
Prüfen Sie usage.txt und mapping.txt für OpoData und prüfen Sie configuration.txt auf -keepattributes Signature |
Verwenden Sie -keep class ... (falls OpoData reflektiv instanziiert wird) mit <init>(...); <fields>; und behalten Sie -keepattributes Signature bei |
UnsatisfiedLinkError oder fehlender native-Callback, wenn SDK .so-Library lädt |
Dynamisch registrierte RegisterNatives-Downcalls wurden geschrumpft oder C/C++-to-Java-Upcall-Zielmethoden wurden von R8 umbenannt |
Inspizieren Sie logcat auf das exakt fehlende JNI-Symbol und prüfen Sie apkanalyzer dex packages und mapping.txt auf die Java-Zielklasse |
Verwenden Sie -keep (oder -keepclassmembers bei direkter Instanziierung der Bridge-Klasse in Java) mit includedescriptorclasses für JNI-Upcall- und RegisterNatives-Ziele |
| Exportiertes Android-Channel-Paket schlägt bei Installation fehl oder verliert Channel-Statistiken | Post-Export-APK-Modifikation machte die APK-Signatur ungültig oder überschrieb eingebettete Channel-Metadaten | Führen Sie zipalign -c -P 16 -v 4 und apksigner verify --verbose auf dem exportierten Channel-APK aus und testen Sie auf einem physischen Gerät |
Führen Sie R8-Minifizierung, Hardening, Alignment und Signierung auf dem Basis-APK vor dem Upload in die Konsole durch; verifizieren Sie Signatur und Attribution nach Export |
Häufig gestellte Fragen (FAQ)
Wie schließe ich ein Mobile SDK von der Android ProGuard- und R8-Obfuscation aus?
Wie verifiziere ich, dass meine Android SDK-Klassen von der R8-Obfuscation ausgeschlossen wurden?
Warum funktionieren Android-Channel-Paketstatistiken nach APK-Hardening oder Nachbearbeitung nicht mehr?
Zusammenfassung und Checkliste für die Release-Build-Verifizierung
Das Verständnis darüber, wie Mobile SDKs von der Android ProGuard- und R8-Obfuscation ausgeschlossen werden, ist essenziell, um unbemerkte Attributions-Regressionen beim Übergang von Debug-Tests zu Produktions-Release-Builds zu verhindern. Durch das Überprüfen gebündelter Consumer-Regeln, das Schreiben eng gefasster -keep-Direktiven, die den exakten case-sensitive Klassenpfaden innerhalb Ihres SDK-Archivs entsprechen, das Einhalten der Build-Reihenfolge für Android-Channel-Pakete vor dem Upload und das Abgleichen der R8-Diagnose-Ausgabedateien mit dem Raw-DEX-Bytecode können Android-Ingenieure ein vollständiges Code-Shrinking beibehalten, ohne Deferred Deep Linking oder Parameter-Wiederherstellungen zu beeinträchtigen.
Bevor Sie einen minimierten Android-Release-Build bei Google Play veröffentlichen oder Channel-Pakete exportieren, führen Sie diese vierstufige Verifizierungs-Checkliste aus:
-
Inspizieren Sie das SDK-Archiv und die zusammengeführte Konfiguration: Prüfen Sie Ihr heruntergeladenes SDK-Archiv (z. B. v2.1.0) mit
jar tfoderunzip -l, um exakte Klassenpfade und gebündelte Consumer-Regeln zu prüfen, und führen Sie./gradlew :app:analyzeReleaseR8Config(in AGP 9.3+) aus oder überprüfen Sieconfiguration.txt. -
Cross-Check von R8-Berichten und Raw-DEX-Bezeichnern: Bestätigen Sie, dass erforderliche SDK-Einstiegspunkte, reflektive
<init>()-Konstruktoren,OpoData-Modellfelder,ResultCallBack-Methoden und JNI-Signaturen inseeds.txterscheinen, nicht inusage.txtvorkommen und ihre ursprünglichen Namen in der Raw-apkanalyzer dex packages-Ausgabe (ausgeführt ohne--proguard-mappings) sowie inmapping.txtbehalten. -
Erzwingen Sie Pre-Upload-Minifizierung und verifizieren Sie Channel-APK-Signaturen: Führen Sie alle R8-Minifizierungen, Drittanbieter-Hardening,
zipalign(zipalign -c -P 16 -v 4) und APK-Signierung auf dem Basis-APK vor dem Upload in die Konsole durch, um Android-Channel-Pakete zu exportieren, und führen Sieapksigner verify --verboseauf exportierten Paketen aus. -
Führen Sie einen Release-Smoke-Test auf einem physischen Gerät aus: Installieren Sie das signierte, minimierte Release-APK auf einem echten Android-Gerät und verifizieren Sie sowohl die H5-Parameter-Installation (
getInstallParam) als auch One-Click-Wakeup (handleDeepLink).
Entwickler-Teams können eine Anwendung registrieren und ihr obfuskierte Basis-APK auf der OpoInstall Developer-Konsole hochladen, um den Channel-Paket-Export zu testen und die Parameter-Wiederherstellung im Release-Build zu verifizieren.
Verwandte Materialien
-
Konzepte: Android R8 Code Shrinking, Bezeichner-Obfuscation, Consumer-ProGuard-Regeln, Channel-Paket-Build-Reihenfolge
-
Technologien: Android Gradle Plugin (AGP), R8-Compiler,
proguard-rules.pro, Android Studio APK Analyzer -
Standards: OWASP Mobile Application Security Project
-
Plattform- und Integrations-Interfaces:
isMinifyEnabled,optimization.enable,analyzeReleaseR8Config,consumerProguardFiles,seeds.txt,usage.txt,apkanalyzer dex packages,apksigner,zipalign -
Offizielle Dokumentation & Referenzen:
Share this article



