ProGuard at R8 — mga tool ng obfuscation, minification at optimization para sa Android app. Ang ProGuard, na nilikha noong 2002, ay matagal nang naging pamantayan ng de facto para sa proteksyon ng Java code. Ang R8 — ang kapalit nito, na binuo ng Google at naka-embed sa Android Gradle Plugin simula AGP 3.4. Ang parehong tool ay nagpapaliit ng laki ng APK, nag-aalis ng patay na code at nagpapahirap sa reverse engineering. Ayon sa Android Developers, ang R8 ay nagsasagawa ng build nang 2–3 beses na mas mabilis kaysa ProGuard sa maihahambing na kalidad ng obfuscation.
Mga Pangunahing Punto
ProGuard — ay isang open-source tool (Apache 2.0) para sa obfuscation, minification, optimization at pre-verification ng Java bytecode. Binuo ni Eric Lafourge noong 2002 sa loob ng proyektong SourceForge. Ang ProGuard ay tumatanggap ng input na pinagsama-samang Java classes (.class) o JAR archive at naglalabas ng mga naprosesong klase ng parehong format, ngunit mas maliit ang laki at may mga elementong pinalitan ng pangalan.
Sa mahabang panahon, ang ProGuard ay nag-iisang pamantayan para sa pagprotekta ng Android app mula sa reverse engineering. Opisyal na inirekomenda ng Google ang paggamit nito sa Android SDK at nagbigay ng default na configuration sa file na proguard-android-optimize.txt sa loob ng SDK tools. Ang ProGuard ay gumana bilang isang hiwalay na tool, na pinapatakbo pagkatapos ng compilation ng Java code sa bytecode at bago ang pag-package sa DEX.
Ang ProGuard ay binubuo ng apat na sunod-sunod na phase: shrink (pag-aalis ng hindi ginagamit na mga klase), optimize (optimization ng bytecode — inlining, pag-aalis ng patay na code), obfuscate (pagpapalit ng pangalan ng mga klase, method at field sa maiikling pangalan), preverify (pagsusuri ng compatibility sa JVM). Bawat phase ay kinokontrol ng hiwalay na mga patakaran mula sa mga configuration file.
Sa yugto ng obfuscation, ang ProGuard ay bumubuo ng mapping file (mapping.txt) na nagma-map ng orihinal na mga pangalan sa obfuscated na mga pangalan. Ang file na ito ay kritikal para sa pag-decode ng crash log mula sa release build sa pamamagitan ng retrace utility. Kung walang mapping file, ang stack trace ay nagiging isang set ng mga titik a(), b(), c() na walang posibilidad na maibalik ang orihinal na konteksto.
| Phase ng ProGuard | Layunin | Resulta |
|---|---|---|
| Shrink | Pagsusuri ng call graph at pag-aalis ng patay na code | Pagbawas ng bilang ng mga klase sa APK |
| Optimize | Inlining ng mga method, pag-aalis ng hindi ginagamit na parameter | Pagpapabilis ng pag-execute ng code |
| Obfuscate | Pagpapalit ng pangalan ng mga klase, field at method | Proteksyon laban sa reverse engineering |
| Preverify | Pagdaragdag ng StackMap attribute para sa JVM | Compatibility sa Java 6+ |
R8 — ay ang susunod na henerasyon na tool ng obfuscation at minification mula sa Google, unang ipinakilala sa Android Studio 3.3 (Nobyembre 2018) at naging pamantayan sa AGP 3.4 (Agosto 2019). Hindi tulad ng ProGuard, ang R8 ay bahagi ng D8/R8 compiler na nagko-convert ng Java bytecode sa DEX format. Ginagawa ng R8 ang lahat ng phase — obfuscation, minification at optimization — sa isang pass, nang walang paglilipat ng mga intermediate file sa pagitan ng mga tool.
Binuo ng Google ang R8 na may dalawang layunin: pabilisin ang build (gumana ang ProGuard bilang panlabas na tool) at tiyakin ang tuluy-tuloy na integration sa modernong Android stack (Desugar, Core Library Desugaring, D8). Ang R8 ay isinulat sa Kotlin at Java at bahagi ng repository na R8/Desugar sa AOSP (Android Open Source Project).
Isang mahalagang bentahe ng R8 — ganap na backward compatibility sa ProGuard rules. Ang umiiral na .pro file ay gumagana nang walang pagbabago. Sinusuportahan pa ng R8 ang mga specific na ProGuard directive, kabilang ang -whyareyoukeeping, -printconfiguration at -printmapping. Ito ay nangangahulugan na ang paglipat mula ProGuard patungong R8 ay nangyayari nang transparent: sapat na upang i-update ang AGP.
// build.gradle.kts — pag-enable ng R8 sa pamamagitan ng minifyEnabled
android {
buildTypes {
getByName("release") {
isMinifyEnabled = true
isShrinkResources = true
proguardFiles(
// Base configuration mula sa Android SDK
getDefaultProguardFile("proguard-android-optimize.txt"),
// Custom na patakaran ng proyekto
"proguard-rules.pro"
)
}
}
}Ipinapakita ng code ang standard na configuration ng release build. Ang flag na isMinifyEnabled = true ay nag-activate ng R8 para sa obfuscation at optimization. Ang isShrinkResources = true ay karagdagang nag-aalis ng hindi ginagamit na resources. Ang getDefaultProguardFile ay naglo-load ng mga base rule mula sa SDK, at ang proguard-rules.pro ay naglalaman ng specific na setting para sa proyekto.
Obfuscation — ay ang proseso ng pag-convert ng source code sa isang anyo na mahirap suriin ng tao, ngunit nagpapanatili ng buong functionality. Sa konteksto ng Android, ang obfuscation ay nangangahulugan ng pagpapalit ng pangalan ng mga klase, method at field sa maiikling, walang kahulugan na pangalan: ang com.example.app.auth.LoginManager ay nagiging a.a.a, ang method na authenticateUser ay nagiging a, ang field na userToken ay nagiging b.
Ang Android APK file ay mga archive na maaaring buksan ng kahit anong archiver (ZIP, 7z, WinRAR). Kung walang obfuscation, makukuha ng attacker ang buong mapa ng app: mga pangalan ng package, klase, method at field. Ang mga tool tulad ng jadx o Bytecode Viewer ay nagre-restore ng halos orihinal na Java code mula sa DEX file sa ilang segundo. Ang obfuscation ay hindi ginagawang hindi vulnerable ang code, ngunit malaki ang pagtaas ng threshold ng pagpasok: sa halip na makabuluhang pangalan, makikita ng mambabasa ang a(), b(), c().
Karaniwang layunin ng obfuscation: proteksyon ng komersyal na lohika (algorithm, formula ng pagkalkula), pagpapahirap sa pagnanakaw ng API key at token, pagpigil sa pagpapalit ng klase sa pamamagitan ng reflection, proteksyon laban sa pag-patch at pagbabago ng APK (repackage attack). Sa praktika, 70% ng mga gawain ay nalulutas sa pamamagitan ng pagpapalit ng pangalan — iyon ang dahilan kung bakit pinapatakbo ang ProGuard / R8.
Sa ibaba ay isang tipikal na proguard-rules.pro file para sa Android project na may Retrofit, Gson at Parcelable. Ang mga patakaran na -keep ay nagpapanatili ng mga klase at method na kinakailangan para sa pagpapatakbo ng mga library sa pamamagitan ng reflection. Kung wala ang mga patakarang ito, aalisin o papalitan ng R8 ng pangalan ang mga klase na ina-access ng library sa pamamagitan ng string name.
# =====================
# Retrofit — pagpapanatili ng interface
# =====================
-keep,allowobfuscation,allowshrinking interface retrofit2.** { *; }
-keepattributes Signature, Exceptions
# =====================
# Gson — serialization ng 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 — pag-aalis ng log mula sa 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 class — pagpapanatili ng constructor
# =====================
-keepclassmembers class * {
@kotlin.Metadata <fields>;
}
# =====================
# Activity — entry point
# =====================
-keep class * extends android.app.Activity {
@android.annotation.SuppressLint <methods>;
}Ang bawat directive sa .pro file ay lumulutas ng isang tiyak na gawain. Ang -keep ay pumipigil sa pag-aalis o pagpapalit ng pangalan ng buong klase. Ang -keepclassmembers ay nagpoprotekta lamang ng mga miyembro ng klase (field at method), ngunit pinapayagan ang pag-aalis ng klase mismo kung hindi ito ginagamit. Ang -assumenosideeffects ay nagpapahiwatig sa R8 na ang pagtawag ng method ay walang side effect at maaaring ligtas na alisin. Ang directive na -keepattributes ay nagpapanatili ng metadata sa bytecode — annotation, signature, exception.
Ang patakaran na -keep,allowobfuscation,allowshrinking para sa Retrofit ay nagpapahintulot sa R8 na palitan ang pangalan ng mga interface, ngunit hindi alisin ang mga ito. Ito ay kinakailangan dahil ang Retrofit ay nag-a-access sa mga interface sa pamamagitan ng dynamic proxy (java.lang.reflect.Proxy), at ang pag-aalis ay magdudulot ng ClassNotFoundException sa runtime. Katulad nito, ginagamit ng Gson ang reflection para ma-access ang mga field na may @SerializedName annotation — kung walang -keepclassmembers, ang mga field ay aalisin bilang hindi ginagamit.
Minification (shrinking) — proseso ng pag-aalis ng hindi ginagamit na code at resources mula sa panghuling build. Sinusuri ng ProGuard at R8 ang call graph, simula sa mga entry point (Activity, Service, BroadcastReceiver), at inaalis ang mga klase at method na hindi maaaring maabot sa pamamagitan ng chain ng tawag. ShrinkResources — karagdagang yugto na nag-aalis ng hindi ginagamit na resources mula sa res/ (layout, drawable, string, color).
Ang minification ay nagbibigay ng pinakamalaking pakinabang sa malalaking proyekto na may mga library. Karaniwang larawan: ang proyekto ay gumagamit ng 10% ng code mula sa nakakonektang library (halimbawa, Google Play Services). Kung walang minification, ang buong code ng library ay napupunta sa APK. Sa minification, inaalis ng R8 ang 70–90% ng code ng library, nag-iiwan lamang ng aktwal na ginagamit na mga klase at method. Ito ay direktang nakakaapekto sa laki ng APK, oras ng pag-load at pagkonsumo ng memorya.
Ang mekanismo ng ShrinkResources ay gumagana kasama ng minification ng code. Pagkatapos matukoy ng R8 kung aling mga klase ang ginagamit, sinusuri ng resource shrinking ang mga reference sa resources mula sa code: R.layout.main, R.drawable.icon, getString(R.string.title). Lahat ng resources na walang direktang o hindi direktang reference ay inaalis mula sa panghuling APK o AAB. Para dito ginagamit ang resource file na resources.arsc at mga folder na res/.
Mahalagang nuance: ang resources ay maaaring tawagin sa pamamagitan ng getIdentifier() o Resources.getResourceName() sa pamamagitan ng string name, na lumalampas sa R class. Sa ganitong mga kaso, hindi nakikita ng R8 ang direktang link at maaaring mag-alis ng resource na aktwal na ginagamit. Para sa proteksyon ng naturang mga resources, mayroong directive na -keep class **.R$* { *; } — pinapanatili nito ang lahat ng identifier ng R class.
<!-- Halimbawa: resource na ginagamit lamang sa pamamagitan ng getIdentifier() -->
<string name="dynamic_title_welcome">Maligayang pagdating</string>
<string name="dynamic_title_share">Ibahagi</string>
<!-- Kotlin code na nag-a-access sa pamamagitan ng string -->
<!-- val title = getString(resources.getIdentifier( -->
<!-- \"dynamic_title_${type}\", \"string\", packageName)) -->Sa kasong ito, hindi nakikita ng R8 ang static reference sa dynamic_title_welcome sa R class, dahil ang pag-access ay nangyayari sa pamamagitan ng getIdentifier na may dynamic na pangalan. Upang mapanatili ang mga naturang resources, kailangang idagdag sa proguard-rules.pro ang directive na -keepclassmembers class **.R$string { *; } — pinagbabawalan nito ang pag-aalis ng anumang field mula sa lahat ng R$string class.
| Directive | Layunin | Halimbawa |
|---|---|---|
| -keep | Pinapanatili ang klase at lahat ng miyembro nito | -keep class com.example.api.** { *; } |
| -keepclassmembers | Pinapanatili lamang ang mga miyembro ng klase | -keepclassmembers class * { @SerializedName <fields>; } |
| -keepattributes | Pinapanatili ang metadata ng bytecode | -keepattributes *Annotation*, Signature |
| -assumenosideeffects | Inaalis ang mga tawag na walang side effect | -assumenosideeffects class Log { d(...); } |
| -dontwarn | Pinipigilan ang mga babala | -dontwarn com.example.legacy.** |
Kahit na ang R8 ay kapalit ng ProGuard, may mga prinsipyong pagkakaiba sa arkitektura, pagganap at pag-uugali sa pagitan ng mga tool. Opisyal na itinigil ng Google ang suporta para sa ProGuard sa Android Gradle Plugin simula AGP 7.0, gayunpaman ang ProGuard ay patuloy na ginagamit sa mga proyekto kung saan kinakailangan ang specific na pag-uugali ng optimization na hindi available sa R8.
| Katangian | ProGuard | R8 |
|---|---|---|
| Developer | GuardSquare (Eric Lafourge) | |
| Taon ng paglabas | 2002 | 2018 (stable noong 2019) |
| Arkitektura | 4 na hiwalay na phase (shrink → optimize → obfuscate → preverify) | Isang pass: shrink + optimize + obfuscate nang sabay |
| Integration sa AGP | Panlabas na tool, pinapatakbo pagkatapos ng javac | Naka-embed sa D8 DEX compiler |
| Bilis ng build | 2–3 beses na mas mabagal | Mas mabilis dahil sa isang pass at native integration |
| Suporta sa Kotlin | Limitado (problema sa inline, lambdas, coroutines) | Ganap: coroutines, inline function, data class |
| Mapping file | mapping.txt (compatible sa retrace) | mapping.txt (parehong format) |
| Pag-customize ng optimization | 60+ option -optimizationpasses, -optimizations | Limitado: karamihan sa optimization ay naka-default na naka-on |
| Status ng suporta | Pinalitan ng R8 (AGP 7.0+ hindi gumagamit) | Aktibong pag-unlad, bahagi ng AOSP |
Ang R8 ay mas agresibo kaysa ProGuard sa pag-aalis ng code na itinuturing nitong patay. Ito ay humahantong sa mga sitwasyon kung saan gumagana ang debug build, ngunit ang release ay nagca-crash sa ClassNotFoundException o NoSuchMethodException. Karaniwang kaso: mga library na gumagamit ng reflection sa pamamagitan ng pangalan ng klase (Gson, Moshi, Retrofit, Room, Dagger); pagtawag ng ServiceLoader o java.util.ServiceLoader; dynamic proxy (java.lang.reflect.Proxy); native method (JNI). Solusyon — magdagdag ng -keep para sa lahat ng klase na tinatawag sa pamamagitan ng reflection.
# Karaniwang problema sa reflection — hindi nakikita ng R8 ang static na link
# Room — pinapanatili ang DAO at migration
-keep class * extends androidx.room.RoomDatabase { *; }
-keep class *.DatabaseMigrations { *; }
# Dagger / Hilt — pinapanatili ang component
-keep class * extends dagger.hilt.android.components.** { *; }
# JNI — hindi pinapalitan ng pangalan ang native method
-keepclasseswithmembernames class * {
native <methods>;
}
# Data Binding — pinapanatili ang Binding class
-keep class *.databinding.** { *; }Kung pagkatapos magdagdag ng mga patakaran ang build ay nagca-crash pa rin, gamitin ang flag na -printconfiguration full-config.txt sa proguard-rules.pro. Ang R8 ay bubuo ng kumpletong configuration file na nagpapakita kung aling mga patakaran ang inilapat at kung aling mga klase ang pinapanatili. Gayundin kapaki-pakinabang ang directive na -whyareyoukeeping class com.example.MyClass — ipinapakita nito ang dahilan kung bakit nagpasya ang R8 na panatilihin ang nasabing klase.
Ang tamang configuration ng ProGuard rules — susi sa matatag na pagpapatakbo ng obfuscation na walang bug sa runtime. Sa ibaba ay ang step-by-step na proseso ng configuration para sa isang bagong proyekto o para sa isang proyekto kung saan ang obfuscation ay nagdudulot ng error.
Magsimula sa pagkonekta ng standard na Android SDK file — proguard-android-optimize.txt. Naglalaman ito ng mga patakaran para sa base Android component: Activity, Service, BroadcastReceiver, ContentProvider, View, Fragment. Ang file na ito ay matatagpuan sa SDK folder: $ANDROID_HOME/tools/proguard/proguard-android-optimize.txt. Kung gumagamit ka ng AGP, awtomatikong ilo-load ito ng getDefaultProguardFile.
Bawat sikat na library ay may inirerekomendang ProGuard rules. Ang Retrofit, OkHttp, Glide, Fresco, Coil, Room, Dagger/Hilt, Kotlin Coroutines — lahat ay nangangailangan ng specific na -keep rules. Karaniwan ang mga patakaran ay kasama sa AAR library at awtomatikong kumokonekta sa pamamagitan ng consumer guard rules. Suriin kung ang library ay nagbibigay ng proguard.txt file sa loob ng AAR — ito ay tanda na ang mga patakaran ay isinasaalang-alang na.
Bago ilathala, kinakailangang subukan ang release build sa isang tunay na device o emulator. Ang mga problema sa obfuscation ay lumilitaw lamang sa runtime. Suriin: authentication (login/registration), pag-load ng data mula sa network, navigation sa pagitan ng mga screen, camera at gallery, push notification, Deeplinks, WebView. Bawat crash sa release build ay dapat i-decode sa pamamagitan ng retrace na may mapping file at magdagdag ng nawawalang -keep rules.
Ang mapping file ay nabuo sa build/outputs/mapping/release/mapping.txt. Ang file na ito ay sapilitang panatilihin: kung wala ito, hindi posible na i-decode ang crash log mula sa Google Play Console. Isama ang mapping.txt sa version control system o i-upload ito bilang CI artifact. Ang Google Play Console ay awtomatikong tumatanggap ng mapping file kapag nag-upload ng AAB na may uploading mapping.txt na naka-on.
Sa ibaba ay ang kumpletong workflow ng configuration ng obfuscation sa file na proguard-rules.pro na may mga komento para sa bawat grupo ng mga patakaran.
# ===========================================
# proguard-rules.pro — kumpletong halimbawa
# ===========================================
# --- Pangkalahatang setting ---
-keepattributes *Annotation*, Signature, Exceptions, InnerClasses, EnclosingMethod
-dontpreverify
# --- Android component ---
-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 {}
# --- Serialization ---
-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 lamang: sapilitang pagpapanatili ---
# (Hindi pinapansin ng ProGuard ang directive na ito)
-keep,allowobfuscation class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator CREATOR;
}Pagkatapos ng configuration, isagawa ang build: ./gradlew assembleRelease. Suriin kung sa build/outputs/mapping/release/ ay lumitaw ang mga file: mapping.txt (tugma ng orihinal at obfuscated na pangalan), seeds.txt (mga klase na pinanatili ng -keep rules), usage.txt (mga klase na inalis sa panahon ng minification). Ang laki ng APK pagkatapos ng obfuscation ay dapat bumaba ng 20–50% depende sa bilang ng mga nakakonektang library.
Mga Madalas Itanong
R8 — kapalit ng ProGuard, binuo ng Google. Ang R8 ay nagsasagawa ng obfuscation, minification at optimization sa isang pass, gumagana nang 2–3 beses na mas mabilis kaysa ProGuard at direktang naka-integrate sa Android Gradle Plugin. Ang ProGuard ay gumagamit ng apat na hiwalay na phase at nangangailangan ng panlabas na pagpapatakbo. Simula AGP 7.0, ang ProGuard ay hindi ginagamit — ang default ay R8 ang gumagana.
Oo, ang R8 ay gumagamit ng parehong ProGuard rules (.pro file). Ang mga directive na -keep, -keepclassmembers, -keepattributes, -assumenosideeffects ay gumagana nang magkapareho. Ang mga base rule ay nagmumula sa proguard-android-optimize.txt mula sa Android SDK, at ang specific para sa mga library (Retrofit, Room, Gson) ay idinadagdag sa proguard-rules.pro ng proyekto. Kung wala ang mga patakarang ito, maaaring alisin ng R8 ang mga klase na kinakailangan para sa pagpapatakbo ng mga library sa pamamagitan ng reflection.
Ang R8 ay naka-default na naka-enable sa Android Gradle Plugin simula AGP 3.4. Para i-activate ang minification, itakda ang isMinifyEnabled = true sa release buildType block ng build.gradle.kts file. Ang karagdagang flag na isShrinkResources = true ay nag-e-enable ng pag-aalis ng hindi ginagamit na resources. Sa gradle.properties, maaaring pilit na i-disable ang R8 sa pamamagitan ng android.enableR8=false, ngunit ito ay hindi inirerekomenda — ang R8 ay mas mabilis at mas stable.
Obfuscation — pagpapalit ng pangalan ng mga klase, method at field sa maiikling walang kahulugan na pangalan (a, b, c). Ang klase na com.example.app.auth.LoginManager ay nagiging a.a.a, ang method na authenticateUser ay nagiging a. Ito ay nagpapahirap sa reverse engineering ng app, ngunit hindi nakakaapekto sa execution logic. Ang ProGuard at R8 ay nagpapalit lamang ng pangalan ng mga elementong hindi protektado ng -keep rules. Ang mapping file ay nagpapanatili ng tugma ng orihinal at obfuscated na pangalan para sa pag-decode ng crash log.
Para sa pag-decode ng stack trace, ginagamit ang retrace utility (bahagi ng ProGuard/R8 SDK). Command: retrace mapping.txt crash-stacktrace.txt. Ang mapping file ay matatagpuan sa build/outputs/mapping/release/mapping.txt. Ang Google Play Console ay sumusuporta din sa pag-upload ng mapping.txt sa paglalathala ng AAB — ang crash log ay awtomatikong na-decode sa console. Kung walang mapping file, ang stack trace ay maglalaman lamang ng obfuscated na pangalan na a.b.c(), na walang silbi para sa debugging.
Buod
Gagawa kami ng mobile application na turnkey
Gumagawa ang IT Sectr ng mga iOS at Android application para sa mga startup at negosyo mula noong 2017. Magpapayo kami sa iyo at magmumungkahi ng pinakamahusay na solusyon.
Basahin din