ProGuard och R8 — verktyg för obfuskering, minifiering och optimering för Android-appar. ProGuard, skapat 2002, var länge de facto-standarden för skydd av Java-kod. R8 — dess efterträdare, utvecklad av Google och inbyggd i Android Gradle Plugin från och med AGP 3.4. Båda verktygen minskar APK-storleken, tar bort död kod och försvårar reverse engineering. Enligt Android Developers utför R8 bygget 2–3 gånger snabbare än ProGuard med jämförbar obfuskeringskvalitet.
Huvudpunkter
ProGuard — är ett öppen källkodsverktyg (Apache 2.0) för obfuskering, minifiering, optimering och förhandskontroll av Java-bytekod. Utvecklat av Eric Lafourge 2002 inom ramen för SourceForge-projektet. ProGuard tar emot kompilerade Java-klasser (.class) eller JAR-arkiv som indata och producerar bearbetade klasser av samma format, men med mindre storlek och omdöpta element.
ProGuard var länge den enda standarden för att skydda Android-appar mot reverse engineering. Google rekommenderade officiellt dess användning i Android SDK och levererade standardkonfigurationen i filen proguard-android-optimize.txt i SDK tools. ProGuard fungerade som ett separat verktyg som kördes efter kompilering av Java-kod till bytekod och före paketering till DEX.
ProGuard består av fyra på varandra följande faser: shrink (borttagning av oanvända klasser), optimize (optimering av bytekod — inlining, borttagning av död kod), obfuscate (omdöpning av klasser, metoder och fält till korta namn), preverify (kontroll av kompatibilitet med JVM). Varje fas styrs av separata regler från konfigurationsfiler.
I obfuskeringsfasen genererar ProGuard en mapping-fil (mapping.txt) som mappar ursprungliga namn till obfuskerade namn. Denna fil är avgörande för att avkoda crashloggar från release-byggen med verktyget retrace. Utan mapping-fil förvandlas stack trace till en uppsättning bokstäver a(), b(), c() utan möjlighet att återställa det ursprungliga sammanhanget.
| ProGuard-fas | Syfte | Resultat |
|---|---|---|
| Shrink | Analys av anropsgraf och borttagning av död kod | Minskning av antalet klasser i APK |
| Optimize | Inlining av metoder, borttagning av oanvända parametrar | Snabbare kodutförande |
| Obfuscate | Omdöpning av klasser, fält och metoder | Skydd mot reverse engineering |
| Preverify | Lägga till StackMap-attribut för JVM | Kompatibilitet med Java 6+ |
R8 — är nästa generations obfuskering och minifieringsverktyg från Google, först introducerat i Android Studio 3.3 (november 2018) och blev standard i AGP 3.4 (augusti 2019). Till skillnad från ProGuard är R8 en del av D8/R8-kompilatorn som omvandlar Java-bytekod till DEX-format. R8 utför alla faser — obfuskering, minifiering och optimering — i en enda passage, utan att överföra mellanliggande filer mellan verktyg.
Google utvecklade R8 med två mål: snabba upp bygget (ProGuard fungerade som externt verktyg) och säkerställa sömlös integration med den moderna Android-stacken (Desugar, Core Library Desugaring, D8). R8 är skrivet i Kotlin och Java och är en del av arkivet R8/Desugar i AOSP (Android Open Source Project).
En viktig fördel med R8 — full bakåtkompatibilitet med ProGuard rules. Befintliga .pro-filer fungerar utan ändringar. R8 stöder till och med ProGuard-specifika direktiv, inklusive -whyareyoukeeping, -printconfiguration och -printmapping. Detta innebär att övergången från ProGuard till R8 sker transparent: det räcker med att uppdatera AGP.
// build.gradle.kts — aktivera R8 via minifyEnabled
android {
buildTypes {
getByName("release") {
isMinifyEnabled = true
isShrinkResources = true
proguardFiles(
// Grundkonfiguration från Android SDK
getDefaultProguardFile("proguard-android-optimize.txt"),
// Anpassade projektregler
"proguard-rules.pro"
)
}
}
}Koden visar standardkonfigurationen för ett release-bygge. Flaggan isMinifyEnabled = true aktiverar R8 för obfuskering och optimering. isShrinkResources = true tar dessutom bort oanvända resurser. getDefaultProguardFile laddar basreglerna från SDK och proguard-rules.pro innehåller projektspecifika inställningar.
Obfuskering — är processen att omvandla källkod till en form som är svår för människor att analysera men som behåller full funktionalitet. I Android-sammanhang innebär obfuskering att byta namn på klasser, metoder och fält till korta, meningslösa namn: com.example.app.auth.LoginManager blir a.a.a, metoden authenticateUser blir a, fältet userToken blir b.
Android APK-filer är arkiv som kan öppnas med vilket arkiveringsprogram som helst (ZIP, 7z, WinRAR). Utan obfuskering får en angripare en fullständig karta över appen: paket-, klass-, metod- och fältnamn. Verktyg som jadx eller Bytecode Viewer återställer nästan original Java-koden från DEX-filer på några sekunder. Obfuskering gör inte koden osårbar, men höjer tröskeln avsevärt: istället för meningsfulla namn ser läsaren a(), b(), c().
Typiska mål för obfuskering: skydd av kommersiell logik (algoritmer, beräkningsformler), försvårande av stöld av API-nycklar och tokens, förhindrande av klassbyte via reflection, skydd mot patchning och modifiering av APK (repackage attack). I praktiken löser 70% av uppgifterna just genom omdöpning — det är därför ProGuard / R8 körs.
Nedan är en typisk proguard-rules.pro-fil för ett Android-projekt med Retrofit, Gson och Parcelable. Reglerna -keep bevarar de klasser och metoder som krävs för att biblioteken ska fungera via reflection. Utan dessa regler kommer R8 att ta bort eller byta namn på klasser som biblioteket kommer åt via strängnamn.
# =====================
# Retrofit — bevara gränssnitt
# =====================
-keep,allowobfuscation,allowshrinking interface retrofit2.** { *; }
-keepattributes Signature, Exceptions
# =====================
# Gson — JSON-serialisering
# =====================
-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 — ta bort loggar från 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 data-klasser — bevara konstruktorer
# =====================
-keepclassmembers class * {
@kotlin.Metadata <fields>;
}
# =====================
# Activity — ingångspunkt
# =====================
-keep class * extends android.app.Activity {
@android.annotation.SuppressLint <methods>;
}Varje direktiv i .pro-filen löser en specifik uppgift. -keep förhindrar borttagning eller omdöpning av en hel klass. -keepclassmembers skyddar endast klassens medlemmar (fält och metoder) men tillåter borttagning av själva klassen om den inte används. -assumenosideeffects anger för R8 att metodanropet inte har bieffekter och kan tas bort på ett säkert sätt. Direktivet -keepattributes bevarar metadata i bytekoden — annotationer, signaturer, undantag.
Regeln -keep,allowobfuscation,allowshrinking för Retrofit tillåter R8 att byta namn på gränssnitt, men inte att ta bort dem. Detta är nödvändigt eftersom Retrofit kommer åt gränssnitt via dynamisk proxy (java.lang.reflect.Proxy) och borttagning skulle leda till ClassNotFoundException vid körning. På samma sätt använder Gson reflection för åtkomst till fält med @SerializedName-annotation — utan -keepclassmembers kommer fälten att tas bort som oanvända.
Minifiering (shrinking) — processen att ta bort oanvänd kod och resurser från det slutliga bygget. ProGuard och R8 analyserar anropsgrafen med början från ingångspunkterna (Activity, Service, BroadcastReceiver) och tar bort klasser och metoder som inte kan nås via anropskedjan. ShrinkResources — en extra fas som tar bort oanvända resurser från res/ (layout, drawable, string, color).
Minifiering ger störst vinst i stora projekt med bibliotek. Typisk bild: projektet använder 10% av koden från ett anslutet bibliotek (t.ex. Google Play Services). Utan minifiering hamnar all bibliotekskod i APK. Med minifiering tar R8 bort 70–90% av bibliotekskoden och lämnar bara faktiskt använda klasser och metoder. Detta påverkar direkt APK-storleken, laddningstiden och minnesförbrukningen.
Mekanismen ShrinkResources arbetar tillsammans med kodminifiering. Efter att R8 har bestämt vilka klasser som används, analyserar resurskrympningen referenser till resurser från koden: R.layout.main, R.drawable.icon, getString(R.string.title). Alla resurser som inte har en direkt eller indirekt referens tas bort från den slutliga APK eller AAB. För detta används resursfilen resources.arsc och mapparna res/.
En viktig nyans: resurser kan anropas via getIdentifier() eller Resources.getResourceName() med strängnamn, vilket förbigår R-klassen. I sådana fall ser R8 inte den direkta kopplingen och kan ta bort en resurs som faktiskt används. För att skydda sådana resurser finns direktivet -keep class **.R$* { *; } — det bevarar alla identifierare i R-klassen.
<!-- Exempel: resurs som endast används via getIdentifier() -->
<string name="dynamic_title_welcome">Välkommen</string>
<string name="dynamic_title_share">Dela</string>
<!-- Kotlin-kod som kommer åt via sträng -->
<!-- val title = getString(resources.getIdentifier( -->
<!-- \"dynamic_title_${type}\", \"string\", packageName)) -->I detta fall ser R8 inte den statiska referensen till dynamic_title_welcome i R-klassen eftersom åtkomsten sker via getIdentifier med ett dynamiskt namn. För att bevara sådana resurser måste direktivet -keepclassmembers class **.R$string { *; } läggas till i proguard-rules.pro — det förbjuder borttagning av alla fält från alla R$string-klasser.
| Direktiv | Syfte | Exempel |
|---|---|---|
| -keep | Bevarar klassen och alla dess medlemmar | -keep class com.example.api.** { *; } |
| -keepclassmembers | Bevarar endast klassens medlemmar | -keepclassmembers class * { @SerializedName <fields>; } |
| -keepattributes | Bevarar bytekodens metadata | -keepattributes *Annotation*, Signature |
| -assumenosideeffects | Tar bort anrop utan bieffekter | -assumenosideeffects class Log { d(...); } |
| -dontwarn | Dämpar varningar | -dontwarn com.example.legacy.** |
Trots att R8 är efterträdare till ProGuard finns det grundläggande skillnader i arkitektur, prestanda och beteende mellan verktygen. Google har officiellt avslutat stödet för ProGuard i Android Gradle Plugin från och med AGP 7.0, men ProGuard används fortfarande i projekt där specifikt optimeringsbeteende krävs som inte är tillgängligt i R8.
| Egenskap | ProGuard | R8 |
|---|---|---|
| Utvecklare | GuardSquare (Eric Lafourge) | |
| Utgivningsår | 2002 | 2018 (stabilt 2019) |
| Arkitektur | 4 separata faser (shrink → optimize → obfuscate → preverify) | En passage: shrink + optimize + obfuscate samtidigt |
| Integrering i AGP | Externt verktyg, körs efter javac | Inbyggd i D8 DEX-kompilatorn |
| Bygghastighet | 2–3 gånger långsammare | Snabbare tack vare en passage och inbyggd integrering |
| Kotlin-stöd | Begränsat (problem med inline, lambdas, coroutines) | Fullt: coroutines, inline-funktioner, data class |
| Mapping-fil | mapping.txt (kompatibel med retrace) | mapping.txt (samma format) |
| Anpassning av optimering | 60+ alternativ -optimizationpasses, -optimizations | Begränsad: de flesta optimeringar är aktiverade som standard |
| Supportstatus | Ersatt av R8 (AGP 7.0+ använder inte) | Aktiv utveckling, del av AOSP |
R8 tar bort kod som det anser vara död mer aggressivt än ProGuard. Detta leder till situationer där debug-bygget fungerar men release kraschar med ClassNotFoundException eller NoSuchMethodException. Typiska fall: bibliotek som använder reflection på klassnamn (Gson, Moshi, Retrofit, Room, Dagger); anrop av ServiceLoader eller java.util.ServiceLoader; dynamiska proxys (java.lang.reflect.Proxy); inbyggda metoder (JNI). Lösning — lägg till -keep för alla klasser som anropas via reflection.
# Typiska problem med reflection — R8 ser inte statisk koppling
# Room — bevara DAO och migreringar
-keep class * extends androidx.room.RoomDatabase { *; }
-keep class *.DatabaseMigrations { *; }
# Dagger / Hilt — bevara komponenter
-keep class * extends dagger.hilt.android.components.** { *; }
# JNI — byt inte namn på inbyggda metoder
-keepclasseswithmembernames class * {
native <methods>;
}
# Data Binding — bevara Binding-klasser
-keep class *.databinding.** { *; }Om bygget fortfarande kraschar efter att reglerna har lagts till, använd flaggan -printconfiguration full-config.txt i proguard-rules.pro. R8 genererar en fullständig konfigurationsfil som visar vilka regler som har tillämpats och vilka klasser som bevaras. Även direktivet -whyareyoukeeping class com.example.MyClass är användbart — det visar orsaken till varför R8 beslutade att bevara den klassen.
Korrekt konfiguration av ProGuard rules — nyckeln till stabil obfuskering utan buggar vid körning. Nedan är steg-för-steg-processen för konfiguration för ett nytt projekt eller för ett projekt där obfuskering orsakar fel.
Börja med att ansluta standardfilen för Android SDK — proguard-android-optimize.txt. Den innehåller regler för grundläggande Android-komponenter: Activity, Service, BroadcastReceiver, ContentProvider, View, Fragment. Denna fil finns i SDK-mappen: $ANDROID_HOME/tools/proguard/proguard-android-optimize.txt. Om du använder AGP laddar getDefaultProguardFile den automatiskt.
Varje populärt bibliotek har rekommenderade ProGuard-regler. Retrofit, OkHttp, Glide, Fresco, Coil, Room, Dagger/Hilt, Kotlin Coroutines — alla kräver specifika -keep-regler. Vanligtvis ingår reglerna i AAR-biblioteket och ansluts automatiskt via consumer guard rules. Kontrollera om biblioteket tillhandahåller filen proguard.txt i AAR — detta är ett tecken på att reglerna redan är beaktade.
Före publicering, testa release-bygget obligatoriskt på en verklig enhet eller emulator. Obfuskeringsproblem uppträder endast vid körning. Kontrollera: autentisering (inloggning/registrering), laddning av data från nätverket, navigering mellan skärmar, kamera och galleri, push-meddelanden, Deeplinks, WebView. Varje krasch i release-bygget måste avkodas via retrace med mapping-filen och saknade -keep-regler måste läggas till.
Mapping-filen genereras i build/outputs/mapping/release/mapping.txt. Denna fil måste sparas: utan den går det inte att avkoda crashloggar från Google Play Console. Inkludera mapping.txt i versionshanteringssystemet eller ladda upp den som CI-artefakt. Google Play Console accepterar mapping-filen automatiskt vid uppladdning av AAB med aktiverad uploading mapping.txt.
Nedan är det fullständiga arbetsflödet för konfiguration av obfuskering i filen proguard-rules.pro med kommentarer för varje regelgrupp.
# ===========================================
# proguard-rules.pro — fullständigt exempel
# ===========================================
# --- Allmänna inställningar ---
-keepattributes *Annotation*, Signature, Exceptions, InnerClasses, EnclosingMethod
-dontpreverify
# --- Android-komponenter ---
-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 {}
# --- Serialisering ---
-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();
}
# --- Endast R8: tvingad bevarande ---
# (ProGuard ignorerar detta direktiv)
-keep,allowobfuscation class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator CREATOR;
}Efter konfiguration, utför bygget: ./gradlew assembleRelease. Kontrollera att filerna har dykt upp i build/outputs/mapping/release/: mapping.txt (motsvarande ursprungliga och obfuskerade namn), seeds.txt (klasser bevarade av -keep-regler), usage.txt (klasser borttagna vid minifiering). APK-storleken efter obfuskering bör minska med 20–50% beroende på antalet anslutna bibliotek.
Vanliga frågor
R8 — efterträdare till ProGuard, utvecklad av Google. R8 utför obfuskering, minifiering och optimering i en enda passage, arbetar 2–3 gånger snabbare än ProGuard och är direkt integrerad i Android Gradle Plugin. ProGuard använder fyra separata faser och kräver extern körning. Från AGP 7.0 används inte ProGuard — som standard fungerar R8.
Ja, R8 använder samma ProGuard rules (.pro-filer). Direktiven -keep, -keepclassmembers, -keepattributes, -assumenosideeffects fungerar identiskt. Basreglerna kommer från proguard-android-optimize.txt från Android SDK och de specifika för bibliotek (Retrofit, Room, Gson) läggs till i projektets proguard-rules.pro. Utan dessa regler kan R8 ta bort klasser som krävs för att biblioteken ska fungera via reflection.
R8 är som standard aktiverat i Android Gradle Plugin från AGP 3.4. För att aktivera minifiering, ställ in isMinifyEnabled = true i blocket release buildType i filen build.gradle.kts. Den extra flaggan isShrinkResources = true aktiverar borttagning av oanvända resurser. I gradle.properties kan R8 tvångsinaktiveras via android.enableR8=false, men detta rekommenderas inte — R8 är snabbare och stabilare.
Obfuskering — omdöpning av klasser, metoder och fält till korta meningslösa namn (a, b, c). Klassen com.example.app.auth.LoginManager blir a.a.a, metoden authenticateUser blir a. Detta försvårar reverse engineering av appen men påverkar inte körningslogiken. ProGuard och R8 byter bara namn på element som inte skyddas av -keep-regler. Mapping-filen bevarar motsvarande ursprungliga och obfuskerade namn för avkodning av crashloggar.
För avkodning av stack trace används verktyget retrace (ingår i ProGuard/R8 SDK). Kommando: retrace mapping.txt crash-stacktrace.txt. Mapping-filen finns i build/outputs/mapping/release/mapping.txt. Google Play Console stöder också uppladdning av mapping.txt vid publicering av AAB — crashloggar avkodas automatiskt i konsolen. Utan mapping-fil kommer stack trace endast att innehålla obfuskerade namn a.b.c(), vilket är oanvändbart för felsökning.
Sammanfattning
Vi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.
Läs också