ProGuard und R8 sind Obfuskations-, Minifikations- und Optimierungswerkzeuge für Android-Anwendungen. ProGuard, 2002 entwickelt, war lange Zeit der De-facto-Standard für den Schutz von Java-Code. R8 ist sein Nachfolger, von Google entwickelt und ab AGP 3.4 in das Android Gradle Plugin integriert. Beide Werkzeuge reduzieren die APK-Größe, entfernen toten Code und erschweren Reverse Engineering. Laut Android Developers führt R8 Builds 2–3 Mal schneller aus als ProGuard bei vergleichbarer Obfuskationsqualität.
Wichtige Punkte
ProGuard ist ein Open-Source-Tool (Apache 2.0) zur Obfuskation, Minifikation, Optimierung und Vorverifizierung von Java-Bytecode. Es wurde 2002 von Eric Lafarge im Rahmen des SourceForge-Projekts entwickelt. ProGuard nimmt kompilierte Java-Klassen (.class) oder JAR-Archive als Eingabe und erzeugt verarbeitete Klassen im gleichen Format, aber mit kleinerer Größe und umbenannten Elementen.
ProGuard war lange Zeit der einzige Standard zum Schutz von Android-Anwendungen vor Reverse Engineering. Google empfahl offiziell die Verwendung im Android SDK und lieferte eine Standardkonfiguration in der Datei proguard-android-optimize.txt innerhalb der SDK-Tools. ProGuard funktionierte als separates Tool, das nach der Kompilierung von Java-Code in Bytecode und vor der Paketierung in DEX ausgeführt wurde.
ProGuard besteht aus vier aufeinanderfolgenden Phasen: shrink (Entfernung ungenutzter Klassen), optimize (Bytecode-Optimierung — Inlining, Entfernung von totem Code), obfuscate (Umbenennung von Klassen, Methoden und Feldern in kurze Namen), preverify (JVM-Kompatibilitätsprüfung). Jede Phase wird durch separate Regeln aus Konfigurationsdateien gesteuert.
Während der Obfuskationsphase erzeugt ProGuard eine Mapping-Datei (mapping.txt), die ursprüngliche Namen auf obfuskatierte Namen abbildet. Diese Datei ist entscheidend für die Dekodierung von Absturzprotokollen aus Release-Builds mit dem Dienstprogramm retrace. Ohne Mapping-Datei wird ein Stack-Trace zu einer Ansammlung von Buchstaben a(), b(), c() ohne Möglichkeit, den ursprünglichen Kontext wiederherzustellen.
| ProGuard-Phase | Zweck | Ergebnis |
|---|---|---|
| Shrink | Call-Graph-Analyse und Entfernung von totem Code | Weniger Klassen in APK |
| Optimize | Methoden-Inlining, Entfernung ungenutzter Parameter | Schnellere Code-Ausführung |
| Obfuscate | Umbenennung von Klassen, Feldern und Methoden | Schutz vor Reverse Engineering |
| Preverify | Hinzufügen von StackMap-Attributen für JVM | Java 6+-Kompatibilität |
R8 ist ein Obfuskations- und Minifikationswerkzeug der nächsten Generation von Google, das erstmals in Android Studio 3.3 (November 2018) vorgestellt wurde und mit AGP 3.4 (August 2019) zum Standard wurde. Im Gegensatz zu ProGuard ist R8 Teil des D8/R8-Compilers, der Java-Bytecode in das DEX-Format konvertiert. R8 führt alle Phasen — Obfuskation, Minifikation und Optimierung — in einem einzigen Durchlauf aus, ohne Zwischendateien zwischen Tools zu übergeben.
Google entwickelte R8 mit zwei Zielen: Builds zu beschleunigen (ProGuard arbeitete als externes Tool) und eine nahtlose Integration mit dem modernen Android-Stack (Desugar, Core Library Desugaring, D8) zu gewährleisten. R8 ist in Kotlin und Java geschrieben und Teil des R8/Desugar-Repositoriums auf AOSP (Android Open Source Project).
Ein wichtiger Vorteil von R8 ist die vollständige Abwärtskompatibilität mit ProGuard-Regeln. Vorhandene .pro-Dateien funktionieren ohne Änderungen. R8 unterstützt sogar spezifische ProGuard-Direktiven, einschließlich -whyareyoukeeping, -printconfiguration und -printmapping. Das bedeutet, dass der Übergang von ProGuard zu R8 transparent ist: einfach AGP aktualisieren.
// build.gradle.kts — Aktivierung von R8 über minifyEnabled
android {
buildTypes {
getByName("release") {
isMinifyEnabled = true
isShrinkResources = true
proguardFiles(
// Basiskonfiguration aus Android SDK
getDefaultProguardFile("proguard-android-optimize.txt"),
// Benutzerdefinierte Projektregeln
"proguard-rules.pro"
)
}
}
}Der Code zeigt eine Standardkonfiguration für Release-Builds. Das Flag isMinifyEnabled = true aktiviert R8 für Obfuskation und Optimierung. isShrinkResources = true entfernt zusätzlich ungenutzte Ressourcen. getDefaultProguardFile lädt die Standardregeln aus dem SDK, während proguard-rules.pro projektspezifische Einstellungen enthält.
Obfuskation ist der Prozess der Umwandlung von Quellcode in eine Form, die für Menschen schwer zu analysieren ist, aber die volle Funktionalität beibehält. Im Android-Kontext bedeutet Obfuskation das Umbenennen von Klassen, Methoden und Feldern in kurze, bedeutungslose Namen: com.example.app.auth.LoginManager wird zu a.a.a, Methode authenticateUser wird zu a, Feld userToken wird zu b.
Android-APK-Dateien sind Archive, die mit jedem Archivierer (ZIP, 7z, WinRAR) geöffnet werden können. Ohne Obfuskation erhält ein Angreifer eine vollständige Karte der Anwendung: Paketnamen, Klassen, Methoden und Felder. Tools wie jadx oder Bytecode Viewer können in Sekunden nahezu original Java-Code aus DEX-Dateien wiederherstellen. Obfuskation macht Code nicht unverwundbar, erhöht aber die Eintrittsbarriere erheblich: Statt aussagekräftiger Namen sieht der Leser a(), b(), c().
Typische Obfuskationsziele: Schutz kommerzieller Logik (Algorithmen, Berechnungsformeln), Erschwerung des Diebstahls von API-Schlüsseln und Tokens, Verhinderung von Klassenersetzung durch Reflection, Verhinderung von APK-Patching und -Modifikation (Repackage-Angriff). In der Praxis werden 70 % der Aufgaben allein durch Umbenennung gelöst — deshalb werden ProGuard/R8 eingesetzt.
Nachfolgend finden Sie eine typische proguard-rules.pro-Datei für ein Android-Projekt mit Retrofit, Gson und Parcelable. Die -keep-Regeln bewahren Klassen und Methoden, die für den Bibliotheksbetrieb über Reflection erforderlich sind. Ohne diese Regeln entfernt oder benennt R8 Klassen um, auf die die Bibliothek per Stringname zugreift.
# =====================
# Retrofit — Erhalt von Schnittstellen
# =====================
-keep,allowobfuscation,allowshrinking interface retrofit2.** { *; }
-keepattributes Signature, Exceptions
# =====================
# Gson — JSON-Serialisierung
# =====================
-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 — Entfernung von Logs aus 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(...);
}
# =====================
# Kotlin-Datenklassen — Erhalt von Konstruktoren
# =====================
-keepclassmembers class * {
@kotlin.Metadata <fields>;
}
# =====================
# Activity — Einstiegspunkt
# =====================
-keep class * extends android.app.Activity {
@android.annotation.SuppressLint <methods>;
}Jede Direktive in einer .pro-Datei löst eine bestimmte Aufgabe. -keep verhindert, dass die gesamte Klasse entfernt oder umbenannt wird. -keepclassmembers schützt nur die Klassenmember (Felder und Methoden), erlaubt aber das Entfernen der Klasse selbst, wenn sie ungenutzt ist. -assumenosideeffects teilt R8 mit, dass ein Methodenaufruf keine Nebenwirkungen hat und sicher entfernt werden kann. Die Direktive -keepattributes bewahrt Metadaten im Bytecode — Annotationen, Signaturen, Ausnahmen.
Die Regel -keep,allowobfuscation,allowshrinking für Retrofit erlaubt R8, Schnittstellen umzubenennen, aber nicht zu entfernen. Dies ist notwendig, da Retrofit über dynamische Proxys (java.lang.reflect.Proxy) auf Schnittstellen zugreift und das Entfernen zur Laufzeit zu ClassNotFoundException führen würde. Ebenso verwendet Gson Reflection, um auf mit @SerializedName annotierte Felder zuzugreifen — ohne -keepclassmembers werden die Felder als ungenutzt entfernt.
Minifikation (Shrinking) ist der Prozess des Entfernens von ungenutztem Code und ungenutzten Ressourcen aus dem endgültigen Build. ProGuard und R8 analysieren den Call-Graphen ausgehend von Einstiegspunkten (Activity, Service, BroadcastReceiver) und entfernen Klassen und Methoden, die über die Aufrufkette nicht erreicht werden können. ShrinkResources ist eine zusätzliche Phase, die ungenutzte Ressourcen aus res/ (Layout, Drawable, String, Color) entfernt.
Die Minifikation bietet den größten Nutzen in großen Projekten mit Bibliotheken. Ein typisches Szenario: Ein Projekt verwendet 10 % des Codes aus einer angebundenen Bibliothek (z. B. Google Play Services). Ohne Minifikation gelangt der gesamte Bibliothekscode in die APK. Mit Minifikation entfernt R8 70–90 % des Bibliothekscodes und hinterlässt nur die tatsächlich verwendeten Klassen und Methoden. Dies wirkt sich direkt auf APK-Größe, Ladezeit und Speicherverbrauch aus.
Der ShrinkResources-Mechanismus arbeitet mit der Code-Minifikation zusammen. Nachdem R8 bestimmt hat, welche Klassen verwendet werden, analysiert das Ressourcen-Shrinking Ressourcenreferenzen aus dem Code: R.layout.main, R.drawable.icon, getString(R.string.title). Alle Ressourcen ohne direkte oder indirekte Referenz werden aus der endgültigen APK oder AAB entfernt. Dies erfolgt mit der Ressourcendatei resources.arsc und den res/-Ordnern.
Eine wichtige Nuance: Ressourcen können über getIdentifier() oder Resources.getResourceName() per Stringnamen aufgerufen werden, unter Umgehung der R-Klasse. In solchen Fällen sieht R8 keine direkte Verbindung und kann eine tatsächlich verwendete Ressource entfernen. Zum Schutz solcher Ressourcen gibt es die Direktive -keep class **.R$* { *; } — sie bewahrt alle Bezeichner der R-Klasse.
<!-- Beispiel: Eine Ressource, die nur über getIdentifier() verwendet wird -->
<string name="dynamic_title_welcome">Willkommen</string>
<string name="dynamic_title_share">Teilen</string>
<!-- Kotlin-Code, der per String zugreift -->
<!-- val title = getString(resources.getIdentifier( -->
<!-- \"dynamic_title_${type}\", \"string\", packageName)) -->In diesem Fall sieht R8 keinen statischen Verweis auf dynamic_title_welcome in der R-Klasse, da der Zugriff über getIdentifier mit einem dynamischen Namen erfolgt. Um solche Ressourcen zu erhalten, fügen Sie die Direktive -keepclassmembers class **.R$string { *; } zu proguard-rules.pro hinzu — sie verhindert das Entfernen aller Felder aus allen R$string-Klassen.
| Direktive | Zweck | Beispiel |
|---|---|---|
| -keep | Bewahrt die Klasse und alle ihre Member | -keep class com.example.api.** { *; } |
| -keepclassmembers | Bewahrt nur die Klassenmember | -keepclassmembers class * { @SerializedName <fields>; } |
| -keepattributes | Bewahrt Bytecode-Metadaten | -keepattributes *Annotation*, Signature |
| -assumenosideeffects | Entfernt Aufrufe ohne Nebenwirkungen | -assumenosideeffects class Log { d(...); } |
| -dontwarn | Unterdrückt Warnungen | -dontwarn com.example.legacy.** |
Obwohl R8 der Nachfolger von ProGuard ist, gibt es grundlegende Unterschiede zwischen den Tools in Architektur, Leistung und Verhalten. Google hat die Unterstützung für ProGuard im Android Gradle Plugin ab AGP 7.0 offiziell eingestellt, aber ProGuard wird weiterhin in Projekten verwendet, die ein bestimmtes Optimierungsverhalten erfordern, das in R8 nicht verfügbar ist.
| Merkmal | ProGuard | R8 |
|---|---|---|
| Entwickler | GuardSquare (Eric Lafarge) | |
| Erscheinungsjahr | 2002 | 2018 (stabil 2019) |
| Architektur | 4 separate Phasen (shrink → optimize → obfuscate → preverify) | Ein Durchlauf: shrink + optimize + obfuscate gleichzeitig |
| AGP-Integration | Externes Tool, nach javac ausgeführt | In den D8 DEX-Compiler integriert |
| Build-Geschwindigkeit | 2–3 Mal langsamer | Schneller durch Einzeldurchlauf und native Integration |
| Kotlin-Unterstützung | Eingeschränkt (Probleme mit inline, Lambdas, Coroutines) | Vollständig: Coroutines, Inline-Funktionen, Data Class |
| Mapping-Datei | mapping.txt (kompatibel mit retrace) | mapping.txt (gleiches Format) |
| Optimierungsanpassung | 60+ Optionen -optimizationpasses, -optimizations | Eingeschränkt: die meisten Optimierungen standardmäßig aktiviert |
| Support-Status | Durch R8 ersetzt (AGP 7.0+ verwendet nicht) | Aktive Entwicklung, Teil von AOSP |
R8 entfernt Code, den es für tot hält, aggressiver als ProGuard. Dies führt zu Situationen, in denen der Debug-Build funktioniert, der Release-Build jedoch mit ClassNotFoundException oder NoSuchMethodException abstürzt. Typische Fälle: Bibliotheken, die Reflection nach Klassennamen verwenden (Gson, Moshi, Retrofit, Room, Dagger); ServiceLoader- oder java.util.ServiceLoader-Aufrufe; dynamische Proxys (java.lang.reflect.Proxy); native Methoden (JNI). Die Lösung besteht darin, -keep für alle Klassen hinzuzufügen, die über Reflection aufgerufen werden.
# Typische Reflexionsprobleme — R8 erkennt keine statische Verknüpfung
# Room — Erhalt von DAO und Migrationen
-keep class * extends androidx.room.RoomDatabase { *; }
-keep class *.DatabaseMigrations { *; }
# Dagger / Hilt — Erhalt von Komponenten
-keep class * extends dagger.hilt.android.components.** { *; }
# JNI — Native Methoden nicht umbenennen
-keepclasseswithmembernames class * {
native <methods>;
}
# Data Binding — Erhalt von Binding-Klassen
-keep class *.databinding.** { *; }Wenn der Build nach dem Hinzufügen von Regeln immer noch abstürzt, verwenden Sie das Flag -printconfiguration full-config.txt in proguard-rules.pro. R8 generiert eine vollständige Konfigurationsdatei, die zeigt, welche Regeln angewendet werden und welche Klassen erhalten bleiben. Ebenfalls nützlich ist die Direktive -whyareyoukeeping class com.example.MyClass — sie gibt den Grund aus, warum R8 beschlossen hat, die betreffende Klasse zu erhalten.
Die richtige Konfiguration der ProGuard-Regeln ist der Schlüssel zu stabiler Obfuskation ohne Laufzeitfehler. Nachfolgend finden Sie eine Schritt-für-Schritt-Einrichtung für ein neues Projekt oder ein Projekt, bei dem die Obfuskation Fehler verursacht.
Beginnen Sie mit der Einbindung der Standard-Android-SDK-Datei proguard-android-optimize.txt. Sie enthält Regeln für grundlegende Android-Komponenten: Activity, Service, BroadcastReceiver, ContentProvider, View, Fragment. Diese Datei befindet sich im SDK-Ordner: $ANDROID_HOME/tools/proguard/proguard-android-optimize.txt. Wenn Sie AGP verwenden, lädt getDefaultProguardFile sie automatisch.
Jede gängige Bibliothek hat empfohlene ProGuard-Regeln. Retrofit, OkHttp, Glide, Fresco, Coil, Room, Dagger/Hilt, Kotlin Coroutines — alle erfordern spezifische -keep-Regeln. Normalerweise sind die Regeln in der AAR-Bibliothek enthalten und werden automatisch über Consumer Guard Rules eingebunden. Überprüfen Sie, ob die Bibliothek eine proguard.txt-Datei innerhalb des AAR bereitstellt — dies zeigt an, dass die Regeln bereits berücksichtigt wurden.
Testen Sie vor der Veröffentlichung den Release-Build unbedingt auf einem echten Gerät oder Emulator. Obfuskationsprobleme treten nur zur Laufzeit auf. Überprüfen Sie: Authentifizierung (Login/Registrierung), Datenladung aus dem Netzwerk, Navigation zwischen Bildschirmen, Kamera und Galerie, Push-Benachrichtigungen, Deeplinks, WebView. Jeder Absturz im Release-Build muss mit retrace und der Mapping-Datei dekodiert werden, und es müssen fehlende -keep-Regeln hinzugefügt werden.
Die Mapping-Datei wird unter build/outputs/mapping/release/mapping.txt generiert. Diese Datei muss aufbewahrt werden: Ohne sie ist es unmöglich, Absturzprotokolle aus der Google Play Console zu dekodieren. Binden Sie mapping.txt in Ihr Versionskontrollsystem ein oder laden Sie es als CI-Artefakt hoch. Die Google Play Console akzeptiert die Mapping-Datei automatisch beim Hochladen eines AAB mit aktiviertem uploading mapping.txt.
Nachfolgend finden Sie einen vollständigen Obfuskations-Einrichtungsworkflow in der Datei proguard-rules.pro mit Kommentaren für jede Regelgruppe.
# ===========================================
# proguard-rules.pro — vollständiges Beispiel
# ===========================================
# --- Allgemeine Einstellungen ---
-keepattributes *Annotation*, Signature, Exceptions, InnerClasses, EnclosingMethod
-dontpreverify
# --- Android-Komponenten ---
-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 {}
# --- Serialisierung ---
-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();
}
# --- Nur R8: erzwungener Erhalt ---
# (ProGuard ignoriert diese Direktive)
-keep,allowobfuscation class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator CREATOR;
}Führen Sie nach der Konfiguration den Build aus: ./gradlew assembleRelease. Überprüfen Sie, ob in build/outputs/mapping/release/ Dateien erschienen sind: mapping.txt (Zuordnung von ursprünglichen zu obfuskierten Namen), seeds.txt (durch -keep-Regeln erhaltene Klassen), usage.txt (während der Minifikation entfernte Klassen). Die APK-Größe nach der Obfuskation sollte je nach Anzahl der angebundenen Bibliotheken um 20–50 % abnehmen.
Häufig gestellte Fragen
R8 ist der Nachfolger von ProGuard, entwickelt von Google. R8 führt Obfuskation, Minifikation und Optimierung in einem Durchlauf aus, arbeitet 2–3 Mal schneller als ProGuard und ist direkt in das Android Gradle Plugin integriert. ProGuard verwendet vier separate Phasen und erfordert externe Ausführung. Ab AGP 7.0 wird ProGuard nicht mehr verwendet — standardmäßig arbeitet R8.
Ja, R8 verwendet die gleichen ProGuard-Regeln (.pro-Dateien). Die Direktiven -keep, -keepclassmembers, -keepattributes, -assumenosideeffects funktionieren identisch. Die Basisregeln befinden sich in proguard-android-optimize.txt aus dem Android SDK, während bibliotheksspezifische Regeln (Retrofit, Room, Gson) in der proguard-rules.pro des Projekts hinzugefügt werden. Ohne diese Regeln kann R8 Klassen entfernen, die für Bibliotheken erforderlich sind, die über Reflection arbeiten.
R8 ist ab AGP 3.4 standardmäßig im Android Gradle Plugin aktiviert. Um die Minifikation zu aktivieren, setzen Sie isMinifyEnabled = true im release buildType-Block der build.gradle.kts-Datei. Das zusätzliche Flag isShrinkResources = true aktiviert die Entfernung ungenutzter Ressourcen. In gradle.properties können Sie R8 über android.enableR8=false erzwingen deaktivieren, dies wird jedoch nicht empfohlen — R8 ist schneller und stabiler.
Obfuskation — Umbenennung von Klassen, Methoden und Feldern in kurze bedeutungslose Namen (a, b, c). Die Klasse com.example.app.auth.LoginManager wird zu a.a.a, die Methode authenticateUser wird zu a. Dies erschwert das Reverse Engineering der Anwendung, beeinflusst aber nicht die Ausführungslogik. ProGuard und R8 benennen nur Elemente um, die nicht durch -keep-Regeln geschützt sind. Die Mapping-Datei bewahrt die Zuordnung von ursprünglichen und obfuskierten Namen zur Dekodierung von Absturzprotokollen.
Zur Dekodierung eines Stack-Trace verwenden Sie das Dienstprogramm retrace (Teil des ProGuard/R8 SDK). Befehl: retrace mapping.txt crash-stacktrace.txt. Die Mapping-Datei befindet sich unter build/outputs/mapping/release/mapping.txt. Die Google Play Console unterstützt auch das Hochladen von mapping.txt bei der Veröffentlichung eines AAB — Absturzprotokolle werden automatisch in der Konsole dekodiert. Ohne Mapping-Datei enthält der Stack-Trace nur obfuskatierte Namen wie a.b.c(), was für das Debugging nutzlos ist.
Zusammenfassung
Wir entwickeln eine mobile Applikation schlüsselfertig
IT Sectr entwickelt seit 2017 iOS- und Android-Apps für Startups und Unternehmen. Wir beraten Sie und schlagen die beste Lösung vor.
Lesen Sie auch