ProGuard a R8 — nástroje pro obfuskaci, minifikaci a optimalizaci pro Android aplikace. ProGuard, vytvořený v roce 2002, byl po dlouhou dobu de facto standardem pro ochranu Java kódu. R8 — jeho nástupce, vyvinutý společností Google a integrovaný do Android Gradle Plugin od AGP 3.4. Oba nástroje zmenšují velikost APK, odstraňují mrtvý kód a ztěžují zpětné inženýrství. Podle Android Developers, R8 provádí sestavení 2–3krát rychleji než ProGuard při srovnatelné kvalitě obfuskace.
Hlavní body
ProGuard — je open-source nástroj (Apache 2.0) pro obfuskaci, minifikaci, optimalizaci a předběžné ověření Java bytekódu. Vyvinutý Ericem Lafourgem v roce 2002 v rámci projektu SourceForge. ProGuard přijímá na vstupu zkompilované Java třídy (.class) nebo JAR archivy a vytváří zpracované třídy stejného formátu, ale menší velikosti s přejmenovanými prvky.
ProGuard byl po dlouhou dobu jediným standardem pro ochranu Android aplikací před zpětným inženýrstvím. Google oficiálně doporučoval jeho použití v Android SDK a dodával výchozí konfiguraci v souboru proguard-android-optimize.txt v SDK tools. ProGuard fungoval jako samostatný nástroj spouštěný po kompilaci Java kódu do bytekódu a před zabalením do DEX.
ProGuard se skládá ze čtyř po sobě jdoucích fází: shrink (odstranění nepoužívaných tříd), optimize (optimalizace bytekódu — inlining, odstranění mrtvého kódu), obfuscate (přejmenování tříd, metod a polí na krátké názvy), preverify (kontrola kompatibility s JVM). Každá fáze je řízena samostatnými pravidly z konfiguračních souborů.
Ve fázi obfuskace ProGuard generuje mapping soubor (mapping.txt), který mapuje původní názvy na obfuskované. Tento soubor je kritický pro dekódování crash logů z release sestavení pomocí nástroje retrace. Bez mapping souboru se stack trace změní na sadu písmen a(), b(), c() bez možnosti obnovení původního kontextu.
| Fáze ProGuard | Účel | Výsledek |
|---|---|---|
| Shrink | Analýza grafu volání a odstranění mrtvého kódu | Snížení počtu tříd v APK |
| Optimize | Inlining metod, odstranění nepoužívaných parametrů | Zrychlení provádění kódu |
| Obfuscate | Přejmenování tříd, polí a metod | Ochrana proti zpětnému inženýrství |
| Preverify | Přidání StackMap atributů pro JVM | Kompatibilita s Java 6+ |
R8 — je nástroj pro obfuskaci a minifikaci nové generace od Google, poprvé představený v Android Studio 3.3 (listopad 2018) a standardem v AGP 3.4 (srpen 2019). Na rozdíl od ProGuard je R8 součástí kompilátoru D8/R8, který převádí Java bytekód do formátu DEX. R8 provádí všechny fáze — obfuskaci, minifikaci i optimalizaci — v jednom průchodu, bez předávání mezisouborů mezi nástroji.
Google vyvinul R8 se dvěma cíli: urychlit sestavení (ProGuard fungoval jako externí nástroj) a zajistit bezproblémovou integraci s moderním Android stackem (Desugar, Core Library Desugaring, D8). R8 je napsán v Kotlin a Java a je součástí repozitáře R8/Desugar v AOSP (Android Open Source Project).
Důležitá výhoda R8 — plná zpětná kompatibilita s ProGuard rules. Stávající .pro soubory fungují beze změn. R8 dokonce podporuje specifické ProGuard direktivy, včetně -whyareyoukeeping, -printconfiguration a -printmapping. To znamená, že přechod z ProGuard na R8 probíhá transparentně: stačí aktualizovat AGP.
// build.gradle.kts — zapnutí R8 pomocí minifyEnabled
android {
buildTypes {
getByName("release") {
isMinifyEnabled = true
isShrinkResources = true
proguardFiles(
// Základní konfigurace z Android SDK
getDefaultProguardFile("proguard-android-optimize.txt"),
// Vlastní pravidla projektu
"proguard-rules.pro"
)
}
}
}Kód demonstruje standardní konfiguraci release sestavení. Příznak isMinifyEnabled = true aktivuje R8 pro obfuskaci a optimalizaci. isShrinkResources = true navíc odstraňuje nepoužívané zdroje. getDefaultProguardFile načítá základní pravidla ze SDK a proguard-rules.pro obsahuje nastavení specifická pro projekt.
Obfuskace — je proces převodu zdrojového kódu do formy, kterou je obtížné analyzovat pro člověka, ale která zachovává plnou funkčnost. V kontextu Android obfuskace znamená přejmenování tříd, metod a polí na krátké, významu zbavené názvy: com.example.app.auth.LoginManager se změní na a.a.a, metoda authenticateUser na a, pole userToken na b.
Soubory APK Android jsou archivy, které lze otevřít libovolným archivátorem (ZIP, 7z, WinRAR). Bez obfuskace získá útočník úplnou mapu aplikace: názvy balíčků, tříd, metod a polí. Nástroje jako jadx nebo Bytecode Viewer obnoví téměř původní Java kód z DEX souborů během několika sekund. Obfuskace nečiní kód nezranitelným, ale výrazně zvyšuje práh vstupu: místo smysluplných názvů čtenář vidí a(), b(), c().
Typické cíle obfuskace: ochrana komerční logiky (algoritmy, výpočetní vzorce), ztížení krádeže API klíčů a tokenů, prevence nahrazování tříd pomocí reflection, ochrana proti patchování a modifikaci APK (repackage attack). V praxi 70% úkolů řeší právě přejmenování — kvůli tomu se spouští ProGuard / R8.
Níže je typický soubor proguard-rules.pro pro Android projekt s Retrofit, Gson a Parcelable. Pravidla -keep uchovávají třídy a metody nezbytné pro fungování knihoven pomocí reflection. Bez těchto pravidel R8 odstraní nebo přejmenuje třídy, ke kterým knihovna přistupuje podle názvu řetězce.
# =====================
# Retrofit — uchování rozhraní
# =====================
-keep,allowobfuscation,allowshrinking interface retrofit2.** { *; }
-keepattributes Signature, Exceptions
# =====================
# Gson — serializace JSON
# =====================
-keepclassmembers class * {
@com.google.gson.annotations.SerializedName <fields>;
}
-keep class *.serialization.** {
<fields>;
}
# =====================
# Parcelable — Creator
# =====================
-keepclassmembers class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator CREATOR;
}
# =====================
# Logging — odstranění logů z 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 třídy — uchování konstruktorů
# =====================
-keepclassmembers class * {
@kotlin.Metadata <fields>;
}
# =====================
# Activity — vstupní bod
# =====================
-keep class * extends android.app.Activity {
@android.annotation.SuppressLint <methods>;
}Každá direktiva v .pro souboru řeší konkrétní úkol. -keep zabraňuje odstranění nebo přejmenování celé třídy. -keepclassmembers chrání pouze členy třídy (pole a metody), ale umožňuje odstranění samotné třídy, pokud se nepoužívá. -assumenosideeffects říká R8, že volání metody nemá vedlejší účinky a může být bezpečně odstraněno. Direktiva -keepattributes uchovává metadata v bytekódu — anotace, signatury, výjimky.
Pravidlo -keep,allowobfuscation,allowshrinking pro Retrofit umožňuje R8 přejmenovávat rozhraní, ale ne je odstraňovat. To je nezbytné, protože Retrofit přistupuje k rozhraním přes dynamický proxy (java.lang.reflect.Proxy) a odstranění by vedlo k ClassNotFoundException za běhu. Podobně Gson používá reflection pro přístup k polím s anotací @SerializedName — bez -keepclassmembers budou pole odstraněna jako nepoužívaná.
Minifikace (shrinking) — proces odstraňování nepoužívaného kódu a zdrojů z konečného sestavení. ProGuard a R8 analyzují graf volání, počínaje vstupními body (Activity, Service, BroadcastReceiver), a odstraňují třídy a metody, ke kterým nelze dospět řetězcem volání. ShrinkResources — dodatečná fáze, která odstraňuje nepoužívané zdroje z res/ (layout, drawable, string, color).
Minifikace přináší největší zisk ve velkých projektech s knihovnami. Typický obrázek: projekt používá 10% kódu z připojené knihovny (např. Google Play Services). Bez minifikace se veškerý kód knihovny dostane do APK. S minifikací R8 odstraňuje 70–90% kódu knihoven a ponechává pouze skutečně používané třídy a metody. To přímo ovlivňuje velikost APK, dobu načítání a spotřebu paměti.
Mechanismus ShrinkResources pracuje ve spojení s minifikací kódu. Poté, co R8 určí, které třídy se používají, zmenšování zdrojů analyzuje odkazy na zdroje z kódu: R.layout.main, R.drawable.icon, getString(R.string.title). Všechny zdroje, na které není přímý nebo nepřímý odkaz, jsou odstraněny z konečného APK nebo AAB. K tomu se používá soubor zdrojů resources.arsc a složky res/.
Důležitá nuance: zdroje mohou být volány prostřednictvím getIdentifier() nebo Resources.getResourceName() podle názvu řetězce, čímž se obchází třída R. V takových případech R8 nevidí přímé spojení a může odstranit zdroj, který je ve skutečnosti používán. K ochraně takových zdrojů existuje direktiva -keep class **.R$* { *; } — uchovává všechny identifikátory třídy R.
<!-- Příklad: zdroj, který se používá pouze přes getIdentifier() -->
<string name="dynamic_title_welcome">Vítejte</string>
<string name="dynamic_title_share">Sdílet</string>
<!-- Kotlin kód přistupující přes řetězec -->
<!-- val title = getString(resources.getIdentifier( -->
<!-- \"dynamic_title_${type}\", \"string\", packageName)) -->V tomto případě R8 nevidí statický odkaz na dynamic_title_welcome ve třídě R, protože přístup probíhá přes getIdentifier s dynamickým názvem. Pro uchování takových zdrojů je třeba přidat do proguard-rules.pro direktivu -keepclassmembers class **.R$string { *; } — zakazuje odstranění libovolných polí ze všech tříd R$string.
| Direktiva | Účel | Příklad |
|---|---|---|
| -keep | Uchovává třídu a všechny její členy | -keep class com.example.api.** { *; } |
| -keepclassmembers | Uchovává pouze členy třídy | -keepclassmembers class * { @SerializedName <fields>; } |
| -keepattributes | Uchovává metadata bytekódu | -keepattributes *Annotation*, Signature |
| -assumenosideeffects | Odstraňuje volání bez vedlejších účinků | -assumenosideeffects class Log { d(...); } |
| -dontwarn | Potlačuje varování | -dontwarn com.example.legacy.** |
Přestože je R8 nástupcem ProGuard, existují mezi nástroji zásadní rozdíly v architektuře, výkonu a chování. Google oficiálně ukončil podporu ProGuard v Android Gradle Plugin od AGP 7.0, avšak ProGuard se nadále používá v projektech, kde je vyžadováno specifické chování optimalizace nedostupné v R8.
| Charakteristika | ProGuard | R8 |
|---|---|---|
| Vývojář | GuardSquare (Eric Lafourge) | |
| Rok vydání | 2002 | 2018 (stabilní 2019) |
| Architektura | 4 samostatné fáze (shrink → optimize → obfuscate → preverify) | Jeden průchod: shrink + optimize + obfuscate současně |
| Integrace do AGP | Externí nástroj, spouštěný po javac | Integrován do D8 DEX kompilátoru |
| Rychlost sestavení | 2–3krát pomalejší | Rychlejší díky jednomu průchodu a nativní integraci |
| Podpora Kotlin | Omezená (problémy s inline, lambdas, coroutines) | Plná: coroutines, inline funkce, data class |
| Mapping soubor | mapping.txt (kompatibilní s retrace) | mapping.txt (stejný formát) |
| Přizpůsobení optimalizace | 60+ možností -optimizationpasses, -optimizations | Omezené: většina optimalizací je ve výchozím nastavení zapnuta |
| Stav podpory | Nahrazen R8 (AGP 7.0+ nepoužívá) | Aktivní vývoj, součást AOSP |
R8 agresivněji než ProGuard odstraňuje kód, který považuje za mrtvý. To vede k situacím, kdy debug sestavení funguje, ale release padá s ClassNotFoundException nebo NoSuchMethodException. Typické případy: knihovny používající reflection podle názvu třídy (Gson, Moshi, Retrofit, Room, Dagger); volání ServiceLoader nebo java.util.ServiceLoader; dynamické proxy (java.lang.reflect.Proxy); nativní metody (JNI). Řešení — přidejte -keep pro všechny třídy, které jsou volány pomocí reflection.
# Typické problémy reflection — R8 nevidí statické propojení
# Room — uchování DAO a migrací
-keep class * extends androidx.room.RoomDatabase { *; }
-keep class *.DatabaseMigrations { *; }
# Dagger / Hilt — uchování komponent
-keep class * extends dagger.hilt.android.components.** { *; }
# JNI — nepřejmenováváme nativní metody
-keepclasseswithmembernames class * {
native <methods>;
}
# Data Binding — uchování Binding tříd
-keep class *.databinding.** { *; }Pokud po přidání pravidel sestavení stále padá, použijte příznak -printconfiguration full-config.txt v proguard-rules.pro. R8 vygeneruje úplný konfigurační soubor, který ukazuje, která pravidla byla použita a které třídy jsou uchovávány. Užitečná je také direktiva -whyareyoukeeping class com.example.MyClass — vypisuje důvod, proč se R8 rozhodl danou třídu uchovat.
Správné nastavení ProGuard rules — klíč ke stabilnímu provozu obfuskace bez chyb za běhu. Níže je postup krok za krokem pro nový projekt nebo pro projekt, ve kterém obfuskace způsobuje chyby.
Začněte připojením standardního souboru Android SDK — proguard-android-optimize.txt. Obsahuje pravidla pro základní Android komponenty: Activity, Service, BroadcastReceiver, ContentProvider, View, Fragment. Tento soubor se nachází ve složce SDK: $ANDROID_HOME/tools/proguard/proguard-android-optimize.txt. Pokud používáte AGP, getDefaultProguardFile jej načte automaticky.
Každá populární knihovna má doporučená ProGuard pravidla. Retrofit, OkHttp, Glide, Fresco, Coil, Room, Dagger/Hilt, Kotlin Coroutines — všechny vyžadují specifická -keep pravidla. Obvykle jsou pravidla zahrnuta v AAR knihovně a připojují se automaticky prostřednictvím consumer guard rules. Zkontrolujte, zda knihovna poskytuje soubor proguard.txt uvnitř AAR — to je známka toho, že pravidla jsou již zohledněna.
Před publikováním nezbytně otestujte release sestavení na skutečném zařízení nebo emulátoru. Problémy obfuskace se projevují pouze za běhu. Zkontrolujte: autentizaci (přihlášení/registraci), načítání dat ze sítě, navigaci mezi obrazovkami, kameru a galerii, push notifikace, Deeplinky, WebView. Každý pád v release sestavení je třeba dekódovat pomocí retrace s mapping souborem a přidat chybějící -keep pravidla.
Mapping soubor je generován v build/outputs/mapping/release/mapping.txt. Tento soubor je povinné uchovat: bez něj nelze dekódovat crash logy z Google Play Console. Zahrňte mapping.txt do systému správy verzí nebo jej nahrajte jako CI artefakt. Google Play Console automaticky přijímá mapping soubor při nahrávání AAB s aktivovaným uploading mapping.txt.
Níže je úplný workflow nastavení obfuskace v souboru proguard-rules.pro s komentáři pro každou skupinu pravidel.
# ===========================================
# proguard-rules.pro — úplný příklad
# ===========================================
# --- Obecná nastavení ---
-keepattributes *Annotation*, Signature, Exceptions, InnerClasses, EnclosingMethod
-dontpreverify
# --- Android komponenty ---
-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 {}
# --- Serializace ---
-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();
}
# --- Pouze R8: vynucené uchování ---
# (ProGuard tuto direktivu ignoruje)
-keep,allowobfuscation class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator CREATOR;
}Po nastavení proveďte sestavení: ./gradlew assembleRelease. Zkontrolujte, zda se v build/outputs/mapping/release/ objevily soubory: mapping.txt (odpovídající původních a obfuskovaných názvů), seeds.txt (třídy uchované pravidly -keep), usage.txt (třídy odstraněné při minifikaci). Velikost APK po obfuskaci by se měla snížit o 20–50% v závislosti na počtu připojených knihoven.
Často kladené otázky
R8 — nástupce ProGuard, vyvinutý společností Google. R8 provádí obfuskaci, minifikaci a optimalizaci v jednom průchodu, pracuje 2–3krát rychleji než ProGuard a je integrován přímo do Android Gradle Plugin. ProGuard používá čtyři samostatné fáze a vyžaduje externí spuštění. Od AGP 7.0 se ProGuard nepoužívá — ve výchozím nastavení pracuje R8.
Ano, R8 používá stejná ProGuard rules (soubory .pro). Direktivy -keep, -keepclassmembers, -keepattributes, -assumenosideeffects fungují identicky. Základní pravidla pocházejí z proguard-android-optimize.txt z Android SDK a specifická pro knihovny (Retrofit, Room, Gson) se přidávají do proguard-rules.pro projektu. Bez těchto pravidel může R8 odstranit třídy nezbytné pro fungování knihoven prostřednictvím reflection.
R8 je ve výchozím nastavení zapnut v Android Gradle Plugin od AGP 3.4. Pro aktivaci minifikace nastavte isMinifyEnabled = true v bloku release buildType souboru build.gradle.kts. Další příznak isShrinkResources = true zapíná odstraňování nepoužívaných zdrojů. V gradle.properties lze R8 vynuceně vypnout pomocí android.enableR8=false, ale to se nedoporučuje — R8 je rychlejší a stabilnější.
Obfuskace — přejmenování tříd, metod a polí na krátké bezvýznamné názvy (a, b, c). Třída com.example.app.auth.LoginManager se změní na a.a.a, metoda authenticateUser na a. To ztěžuje zpětné inženýrství aplikace, ale neovlivňuje logiku provádění. ProGuard a R8 přejmenovávají pouze prvky, které nejsou chráněny pravidly -keep. Mapping soubor uchovává odpovídající původní a obfuskované názvy pro dekódování crash logů.
Pro dekódování stack trace se používá nástroj retrace (součást ProGuard/R8 SDK). Příkaz: retrace mapping.txt crash-stacktrace.txt. Mapping soubor se nachází v build/outputs/mapping/release/mapping.txt. Google Play Console také podporuje nahrávání mapping.txt při publikování AAB — crash logy jsou automaticky dekódovány v konzoli. Bez mapping souboru bude stack trace obsahovat pouze obfuskované názvy a.b.c(), což je k ladění nepoužitelné.
Shrnutí
Vyvineme mobilní aplikaci na klíč
IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.
Přečtěte si také