ProGuard/R8: суштина, обфускација и заштита Android апликација

Аутор: IT Sectr Објављено: 2026-02-14 Време читања: 8 мин

ProGuard и R8 — алати за обфускацију, минификацију и оптимизацију за Android апликације. ProGuard, створен 2002. године, дуго је био дефакто стандард за заштиту Java кода. R8 — његов наследник, развијен од стране Google и уграђен у Android Gradle Plugin почев од AGP 3.4. Оба алата смањују величину APK, уклањају мртав код и отежавају реверзни инжењеринг. Према Android Developers, R8 извршава изградњу 2–3 пута брже од ProGuard уз упоредив квалитет обфускације.

Главно

  • ProGuard — алат за обфускацију и оптимизацију Java бајткода, стандард за Android од 2000-их
  • R8 — наследник ProGuard од Google, уграђен у AGP, обавља обфускацију, минификацију и оптимизацију у једном пролазу
  • Обфускација преименује класе и методе у кратке називе, отежавајући реверзни инжењеринг апликације
  • Минификација уклања некоришћене класе, методе и поља, смањујући величину коначног APK/AAB
  • ProGuard rules (.pro датотеке) управљају тиме који делови кода се чувају, а који обфускују или уклањају

Шта је ProGuard?

ProGuard — је алат отвореног кода (Apache 2.0) за обфускацију, минификацију, оптимизацију и преверификацију Java бајткода. Развио га је Ерик Лафорж 2002. године у оквиру пројекта SourceForge. ProGuard прима на улаз компилиране Java класе (.class) или JAR архиве и производи обрађене класе истог формата, али мање величине и са преименованим елементима.

Дуго времена ProGuard је био једини стандард за заштиту Android апликација од реверзног инжењеринга. Google је званично препоручивао његово коришћење у Android SDK и испоручивао подразумевану конфигурацију у датотеци proguard-android-optimize.txt унутар SDK tools. ProGuard је радио као засебан алат, покретан након компилације Java кода у бајткод и пре паковања у DEX.

Архитектура ProGuard

ProGuard се састоји од четири узастопне фазе: shrink (уклањање некоришћених класа), optimize (оптимизација бајткода — инлајнинг, уклањање мртвог кода), obfuscate (преименовање класа, метода и поља у кратке називе), preverify (провера компатибилности са JVM). Сваком фазом управљају засебна правила из конфигурационих датотека.

У фази обфускације ProGuard генерише mapping датотеку (mapping.txt) која мапира оригиналне називе у обфускиране. Ова датотека је критична за декодирање crash логова из release верзија путем алатке retrace. Без mapping датотеке, stack trace се претвара у скуп слова a(), b(), c() без могућности обнављања оригиналног контекста.

Фаза ProGuardНаменаРезултат
ShrinkАнализа графа позива и уклањање мртвог кодаСмањење броја класа у APK
OptimizeИнлајнинг метода, уклањање некоришћених параметараУбрзање извршења кода
ObfuscateПреименовање класа, поља и методаЗаштита од реверзног инжењеринга
PreverifyДодавање StackMap атрибута за JVMКомпатибилност са Java 6+

Шта је R8?

R8 — је алат за обфускацију и минификацију следеће генерације од Google, први пут представљен у Android Studio 3.3 (новембар 2018) и постао стандардан у AGP 3.4 (август 2019). За разлику од ProGuard, R8 је део D8/R8 компајлера који претвара Java бајткод у DEX формат. R8 извршава све фазе — обфускацију, минификацију и оптимизацију — у једном пролазу, без преношења међуфајлова између алата.

Google је развио R8 са два циља: убрзати изградњу (ProGuard је радио као спољни алат) и обезбедити беспрекорну интеграцију са савременим Android стеком (Desugar, Core Library Desugaring, D8). R8 је написан у Kotlin и Java и део је репозиторијума R8/Desugar у AOSP (Android Open Source Project).

Важна предност R8 — потпуна уназад компатибилност са ProGuard rules. Постојеће .pro датотеке раде без измена. R8 чак подржава специфичне ProGuard директиве, укључујући -whyareyoukeeping, -printconfiguration и -printmapping. То значи да прелазак са ProGuard на R8 пролази транспарентно: довољно је ажурирати AGP.

kotlin
// build.gradle.kts — укључивање R8 путем minifyEnabled
android {
    buildTypes {
        getByName("release") {
            isMinifyEnabled = true
            isShrinkResources = true

            proguardFiles(
                // Основна конфигурација из Android SDK
                getDefaultProguardFile("proguard-android-optimize.txt"),
                // Прилагођена правила пројекта
                "proguard-rules.pro"
            )
        }
    }
}

Код приказује стандардну конфигурацију release изградње. Застава isMinifyEnabled = true активира R8 за обфускацију и оптимизацију. isShrinkResources = true додатно уклања некоришћене ресурсе. getDefaultProguardFile учитава основна правила из SDK, а proguard-rules.pro садржи подешавања специфична за пројекат.

Обфускација кода у Android

Обфускација — је процес претварања изворног кода у облик који је тешко анализирати човеку, али који задржава потпуну функционалност. У контексту Android, обфускација значи преименовање класа, метода и поља у кратке, бесмислене називе: com.example.app.auth.LoginManager се претвара у a.a.a, метод authenticateUser — у a, поље userToken — у b.

Зашто је потребна обфускација

Android APK датотеке су архиве које се могу отворити било којим архиватором (ZIP, 7z, WinRAR). Без обфускације, нападач добија потпуну мапу апликације: називе пакета, класа, метода и поља. Алати попут jadx или Bytecode Viewer обнављају готово оригинални Java код из DEX датотека за неколико секунди. Обфускација не чини код нерањивим, али значајно подиже праг уласка: уместо смислених имена, читалац види a(), b(), c().

Типични циљеви обфускације: заштита комерцијалне логике (алгоритми, формуле прорачуна), отежавање крађе API кључева и токена, спречавање замене класа путем reflection, заштита од patching и модификације APK (repackage attack). У пракси 70% задатака решава управо преименовање — због тога се покреће ProGuard / R8.

Пример ProGuard правила

Испод је типична датотека proguard-rules.pro за Android пројекат са Retrofit, Gson и Parcelable. Правила -keep чувају класе и методе неопходне за рад библиотека путем reflection. Без ових правила, R8 ће уклонити или преименовати класе којима библиотека приступа по имену ниске.

pro
# =====================
# Retrofit — чување интерфејса
# =====================
-keep,allowobfuscation,allowshrinking interface retrofit2.** { *; }
-keepattributes Signature, Exceptions

# =====================
# Gson — серијализација 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 — уклањање логова из 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 дата класе — чувамо конструкторе
# =====================
-keepclassmembers class * {
    @kotlin.Metadata <fields>;
}

# =====================
# Activity — улазна тачка
# =====================
-keep class * extends android.app.Activity {
    @android.annotation.SuppressLint <methods>;
}

Свака директива у .pro датотеци решава конкретан задатак. -keep спречава уклањање или преименовање целе класе. -keepclassmembers штити само чланове класе (поља и методе), али дозвољава уклањање саме класе ако се не користи. -assumenosideeffects указује R8 да позив метода нема споредних ефеката и да се може безбедно уклонити. Директива -keepattributes чува метаподатке у бајткоду — анотације, потписе, изузетке.

Правило -keep,allowobfuscation,allowshrinking за Retrofit дозвољава R8 да преименује интерфејсе, али не и да их уклони. Ово је неопходно јер Retrofit приступа интерфејсима путем динамичког проксија (java.lang.reflect.Proxy), а уклањање би довело до ClassNotFoundException у runtime. Слично, Gson користи reflection за приступ пољима са анотацијом @SerializedName — без -keepclassmembers поља ће бити уклоњена као некоришћена.

Минификација и ShrinkResources

Минификација (shrinking) — процес уклањања некоришћеног кода и ресурса из коначне изградње. ProGuard и R8 анализирају граф позива, почев од улазних тачака (Activity, Service, BroadcastReceiver), и уклањају класе и методе до којих се не може доћи ланцем позива. ShrinkResources — додатна фаза која уклања некоришћене ресурсе из res/ (layout, drawable, string, color).

Минификација даје највећу добит у великим пројектима са библиотекама. Типична слика: пројекат користи 10% кода из прикључене библиотеке (нпр. Google Play Services). Без минификације, цео код библиотеке улази у APK. Са минификацијом, R8 уклања 70–90% кода библиотека, остављајући само стварно коришћене класе и методе. Ово директно утиче на величину APK, време учитавања и потрошњу меморије.

ShrinkResources у акцији

Механизам ShrinkResources ради у пару са минификацијом кода. Након што R8 одреди које класе се користе, смањивање ресурса анализира референце на ресурсе из кода: R.layout.main, R.drawable.icon, getString(R.string.title). Сви ресурси који немају директну или индиректну референцу уклањају се из коначног APK или AAB. За то се користи датотека ресурса resources.arsc и фолдери res/.

Важна нијанса: ресурси могу бити позвани путем getIdentifier() или Resources.getResourceName() по имену ниске, заобилазећи R класу. У таквим случајевима R8 не види директну везу и може уклонити ресурс који се заправо користи. За заштиту таквих ресурса постоји директива -keep class **.R$* { *; } — она чува све идентификаторе R класе.

xml
<!-- Пример: ресурс који се користи само путем getIdentifier() -->
<string name="dynamic_title_welcome">Добродошли</string>
<string name="dynamic_title_share">Подели</string>

<!-- Kotlin код који приступа по ниски -->
<!-- val title = getString(resources.getIdentifier( -->
<!--     \"dynamic_title_${type}\", \"string\", packageName)) -->

У овом случају R8 не види статичку референцу на dynamic_title_welcome у R класи, јер приступ иде преко getIdentifier са динамичким именом. Да би се сачували такви ресурси, потребно је додати у proguard-rules.pro директиву -keepclassmembers class **.R$string { *; } — она забрањује уклањање било којих поља из свих R$string класа.

ДирективаНаменаПример
-keepЧува класу и све њене чланове-keep class com.example.api.** { *; }
-keepclassmembersЧува само чланове класе-keepclassmembers class * { @SerializedName <fields>; }
-keepattributesЧува метаподатке бајткода-keepattributes *Annotation*, Signature
-assumenosideeffectsУклања позиве без споредних ефеката-assumenosideeffects class Log { d(...); }
-dontwarnПотискује упозорења-dontwarn com.example.legacy.**

R8 vs ProGuard: кључне разлике

Иако је R8 наследник ProGuard, постоје принципијелне разлике у архитектури, перформансама и понашању између алата. Google је званично прекинуо подршку за ProGuard у Android Gradle Plugin почев од AGP 7.0, међутим ProGuard се и даље користи у пројектима где је потребно специфично понашање оптимизације недоступно у R8.

Упоредна табела

КарактеристикаProGuardR8
ПрограмерGuardSquare (Ерик Лафорж)Google
Година изласка20022018 (стабилан 2019)
Архитектура4 одвојене фазе (shrink → optimize → obfuscate → preverify)Један пролаз: shrink + optimize + obfuscate истовремено
Интеграција у AGPСпољни алат, покреће се након javacУграђен у D8 DEX компајлер
Брзина изградњеСпорији 2–3 путаБржи захваљујући једном пролазу и изворној интеграцији
Подршка за KotlinОграничена (проблеми са inline, lambdas, coroutines)Потпуна: coroutines, inline-функције, data class
Mapping датотекаmapping.txt (компатибилан са retrace)mapping.txt (исти формат)
Прилагођавање оптимизације60+ опција -optimizationpasses, -optimizationsОграничено: већина оптимизација укључена подразумевано
Статус подршкеЗамењен са R8 (AGP 7.0+ не користи)Активан развој, део AOSP

Када R8 може покварити изградњу

R8 агресивније од ProGuard уклања код који сматра мртвим. То доводи до ситуација када debug изградња ради, а release пада са ClassNotFoundException или NoSuchMethodException. Типични случајеви: библиотеке које користе reflection по имену класе (Gson, Moshi, Retrofit, Room, Dagger); позиви ServiceLoader или java.util.ServiceLoader; динамички прокси (java.lang.reflect.Proxy); изворни методи (JNI). Решење — додати -keep за све класе које се позивају путем reflection.

pro
# Типични проблеми reflection — R8 не види статичку везу

# Room — чувамо DAO и миграције
-keep class * extends androidx.room.RoomDatabase { *; }
-keep class *.DatabaseMigrations { *; }

# Dagger / Hilt — чувамо компоненте
-keep class * extends dagger.hilt.android.components.** { *; }

# JNI — не преименујемо изворне методе
-keepclasseswithmembernames class * {
    native <methods>;
}

# Data Binding — чувамо Binding класе
-keep class *.databinding.** { *; }

Ако након додавања правила изградња и даље пада, користите заставу -printconfiguration full-config.txt у proguard-rules.pro. R8 ће генерисати потпуну конфигурациону датотеку која показује која су правила примењена и које се класе чувају. Такође је корисна директива -whyareyoukeeping class com.example.MyClass — она приказује разлог због којег је R8 одлучио да сачува дату класу.

Подешавање ProGuard rules

Правилно подешавање ProGuard rules — кључ за стабилан рад обфускације без грешака у runtime. Испод је процес подешавања корак по корак за нови пројекат или за пројекат у којем обфускација изазива грешке.

Корак 1: Основна конфигурација

Почните са повезивањем стандардне датотеке Android SDK — proguard-android-optimize.txt. Она садржи правила за основне Android компоненте: Activity, Service, BroadcastReceiver, ContentProvider, View, Fragment. Ова датотека се налази у SDK фолдеру: $ANDROID_HOME/tools/proguard/proguard-android-optimize.txt. Ако користите AGP, getDefaultProguardFile ће је учитати аутоматски.

Корак 2: Библиотеке

Свака популарна библиотека има препоручена ProGuard правила. Retrofit, OkHttp, Glide, Fresco, Coil, Room, Dagger/Hilt, Kotlin Coroutines — све захтевају специфична -keep правила. Обично су правила укључена у AAR библиотеку и повезују се аутоматски путем consumer guard rules. Проверите да ли библиотека пружа датотеку proguard.txt унутар AAR — то је знак да су правила већ узета у обзир.

Корак 3: Тестирање release изградње

Пре објављивања обавезно тестирајте release изградњу на стварном уређају или емулатору. Проблеми обфускације се појављују само у runtime. Проверите: ауторизацију (пријава/регистрација), учитавање података из мреже, навигацију између екрана, камеру и галерију, push обавештења, Deeplinks, WebView. Сваки crash у release изградњи треба декодирати путем retrace са mapping датотеком и додати недостајућа -keep правила.

Корак 4: Mapping датотека и CI

Mapping датотека се генерише у build/outputs/mapping/release/mapping.txt. Ова датотека је обавезна за чување: без ње није могуће декодирати crash логове из Google Play Console. Укључите mapping.txt у систем контроле верзија или га отпремите као CI артефакт. Google Play Console прихвата mapping датотеку аутоматски при отпремању AAB са укљученим uploading mapping.txt.

Испод је потпуни workflow подешавања обфускације у датотеци proguard-rules.pro са коментарима за сваку групу правила.

pro
# ===========================================
# proguard-rules.pro — потпун пример
# ===========================================

# --- Општа подешавања ---
-keepattributes *Annotation*, Signature, Exceptions, InnerClasses, EnclosingMethod
-dontpreverify

# --- Android компоненте ---
-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 {}

# --- Серијализација ---
-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();
}

# --- Само R8: принудно чување ---
# (ProGuard игнорише ову директиву)
-keep,allowobfuscation class * implements android.os.Parcelable {
    public static final android.os.Parcelable$Creator CREATOR;
}

Након подешавања извршите изградњу: ./gradlew assembleRelease. Проверите да су се у build/outputs/mapping/release/ појавиле датотеке: mapping.txt (кореспонденција оригиналних и обфускираних имена), seeds.txt (класе сачуване правилима -keep), usage.txt (класе уклоњене при минификацији). Величина APK након обфускације треба да се смањи за 20–50% у зависности од броја прикључених библиотека.

Често постављана питања

По чему се R8 разликује од ProGuard?

R8 — наследник ProGuard, развијен од Google. R8 обавља обфускацију, минификацију и оптимизацију у једном пролазу, ради 2–3 пута брже од ProGuard и интегрисан је директно у Android Gradle Plugin. ProGuard користи четири одвојене фазе и захтева спољно покретање. Почев од AGP 7.0 ProGuard се не користи — подразумевано ради R8.

Да ли је потребно писати ProGuard rules када се користи R8?

Да, R8 користи иста ProGuard rules (.pro датотеке). Директиве -keep, -keepclassmembers, -keepattributes, -assumenosideeffects раде идентично. Основна правила долазе у proguard-android-optimize.txt из Android SDK, а специфична за библиотеке (Retrofit, Room, Gson) додају се у proguard-rules.pro пројекта. Без ових правила R8 може уклонити класе неопходне за рад библиотека путем reflection.

Како укључити R8 у Android пројекту?

R8 је подразумевано укључен у Android Gradle Plugin почев од AGP 3.4. За активацију минификације поставите isMinifyEnabled = true у release buildType блоку датотеке build.gradle.kts. Додатна застава isShrinkResources = true укључује уклањање некоришћених ресурса. У gradle.properties се може принудно искључити R8 путем android.enableR8=false, али то није препоручљиво — R8 је бржи и стабилнији.

Шта је обфускација кода у Android?

Обфускација — преименовање класа, метода и поља у кратке бесмислене називе (a, b, c). Класа com.example.app.auth.LoginManager постаје a.a.a, метод authenticateUser постаје a. То отежава реверзни инжењеринг апликације, али не утиче на логику извршења. ProGuard и R8 преименују само елементе који нису заштићени правилима -keep. Mapping датотека чува кореспонденцију оригиналних и обфускираних имена за декодирање crash логова.

Како отклонити crash-лог из обфускиране апликације?

За декодирање stack trace користи се алатка retrace (у саставу ProGuard/R8 SDK). Команда: retrace mapping.txt crash-stacktrace.txt. Mapping датотека се налази у build/outputs/mapping/release/mapping.txt. Google Play Console такође подржава отпремање mapping.txt при објављивању AAB — crash логови се аутоматски декодирају у конзоли. Без mapping датотеке stack trace ће садржати само обфускирана имена a.b.c(), што је бескорисно за отклањање грешака.

Завршни закључци

  • ProGuard — класичан алат за обфускацију и оптимизацију Java бајткода, састављен од четири узастопне фазе
  • R8 — модеран наследник од Google, уграђен у AGP, извршава све фазе у једном пролазу са 2–3 пута већим перформансама
  • Обфускација преименује класе, методе и поља у кратке називе, отежавајући реверзни инжењеринг и штитећи комерцијалну логику апликације
  • Минификација уклања некоришћени код и ресурсе, смањујући величину APK за 20–50% у типичним пројектима
  • ProGuard rules (.pro датотеке) управљају понашањем обфускације — директиве -keep, -keepclassmembers, -assumenosideeffects одређују који елементи се чувају, уклањају или преименују
  • Mapping датотека (mapping.txt) — критично важан артефакт изградње за декодирање crash логова из release верзија путем retrace
  • Тестирање release изградње на стварном уређају је обавезно — проблеми обфускације се појављују само у runtime и захтевају додавање недостајућих -keep правила

Развићемо мобилну апликацију под кључ

IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.

Разговарајте о пројекту

Прочитајте такође