ProGuard dan R8 — alat obfuscation, minifikasi dan optimalisasi untuk aplikasi Android. ProGuard, dibuat pada tahun 2002, telah lama menjadi standar de facto untuk melindungi kode Java. R8 — penerusnya, dikembangkan oleh Google dan terintegrasi dalam Android Gradle Plugin mulai AGP 3.4. Kedua alat ini mengurangi ukuran APK, menghapus kode mati dan mempersulit reverse engineering. Menurut Android Developers, R8 melakukan build 2–3 kali lebih cepat daripada ProGuard dengan kualitas obfuscation yang sebanding.
Poin Utama
ProGuard — adalah alat sumber terbuka (Apache 2.0) untuk obfuscation, minifikasi, optimalisasi dan pra-verifikasi bytecode Java. Dikembangkan oleh Eric Lafourge pada tahun 2002 dalam kerangka proyek SourceForge. ProGuard menerima input kelas Java yang telah dikompilasi (.class) atau arsip JAR dan menghasilkan kelas yang telah diproses dengan format yang sama, namun berukuran lebih kecil dan dengan elemen yang telah diganti namanya.
Untuk waktu yang lama, ProGuard adalah satu-satunya standar untuk melindungi aplikasi Android dari reverse engineering. Google secara resmi merekomendasikan penggunaannya di Android SDK dan menyediakan konfigurasi default dalam file proguard-android-optimize.txt di dalam SDK tools. ProGuard berfungsi sebagai alat terpisah, dijalankan setelah kompilasi kode Java menjadi bytecode dan sebelum pengemasan ke DEX.
ProGuard terdiri dari empat fase berurutan: shrink (penghapusan kelas yang tidak digunakan), optimize (optimalisasi bytecode — inlining, penghapusan kode mati), obfuscate (penggantian nama kelas, metode dan field menjadi nama pendek), preverify (pemeriksaan kompatibilitas dengan JVM). Setiap fase dikendalikan oleh aturan terpisah dari file konfigurasi.
Pada tahap obfuscation, ProGuard menghasilkan file mapping (mapping.txt) yang memetakan nama asli ke nama yang diobfusksi. File ini sangat penting untuk mendekode log crash dari build release melalui utilitas retrace. Tanpa file mapping, stack trace berubah menjadi kumpulan huruf a(), b(), c() tanpa kemungkinan memulihkan konteks asli.
| Fase ProGuard | Tujuan | Hasil |
|---|---|---|
| Shrink | Analisis graf panggilan dan penghapusan kode mati | Pengurangan jumlah kelas di APK |
| Optimize | Inlining metode, penghapusan parameter yang tidak digunakan | Percepatan eksekusi kode |
| Obfuscate | Penggantian nama kelas, field dan metode | Perlindungan dari reverse engineering |
| Preverify | Penambahan atribut StackMap untuk JVM | Kompatibilitas dengan Java 6+ |
R8 — adalah alat obfuscation dan minifikasi generasi berikutnya dari Google, pertama kali diperkenalkan di Android Studio 3.3 (November 2018) dan menjadi standar di AGP 3.4 (Agustus 2019). Berbeda dengan ProGuard, R8 adalah bagian dari kompiler D8/R8 yang mengubah bytecode Java menjadi format DEX. R8 melakukan semua fase — obfuscation, minifikasi dan optimalisasi — dalam satu kali proses, tanpa mentransfer file perantara antar alat.
Google mengembangkan R8 dengan dua tujuan: mempercepat build (ProGuard berfungsi sebagai alat eksternal) dan memastikan integrasi yang mulus dengan tumpukan Android modern (Desugar, Core Library Desugaring, D8). R8 ditulis dalam Kotlin dan Java dan merupakan bagian dari repositori R8/Desugar di AOSP (Android Open Source Project).
Keuntungan penting dari R8 — kompatibilitas mundur penuh dengan ProGuard rules. File .pro yang ada berfungsi tanpa perubahan. R8 bahkan mendukung direktif khusus ProGuard, termasuk -whyareyoukeeping, -printconfiguration dan -printmapping. Ini berarti transisi dari ProGuard ke R8 terjadi secara transparan: cukup perbarui AGP.
// build.gradle.kts — mengaktifkan R8 melalui minifyEnabled
android {
buildTypes {
getByName("release") {
isMinifyEnabled = true
isShrinkResources = true
proguardFiles(
// Konfigurasi dasar dari Android SDK
getDefaultProguardFile("proguard-android-optimize.txt"),
// Aturan kustom proyek
"proguard-rules.pro"
)
}
}
}Kode menunjukkan konfigurasi standar build release. Bendera isMinifyEnabled = true mengaktifkan R8 untuk obfuscation dan optimalisasi. isShrinkResources = true juga menghapus sumber daya yang tidak digunakan. getDefaultProguardFile memuat aturan dasar dari SDK, dan proguard-rules.pro berisi pengaturan khusus proyek.
Obfuscation — adalah proses mengubah kode sumber menjadi bentuk yang sulit dianalisis oleh manusia, namun tetap mempertahankan fungsionalitas penuh. Dalam konteks Android, obfuscation berarti mengganti nama kelas, metode dan field menjadi nama pendek tanpa makna: com.example.app.auth.LoginManager berubah menjadi a.a.a, metode authenticateUser menjadi a, field userToken menjadi b.
File APK Android adalah arsip yang dapat dibuka dengan pengarsip apapun (ZIP, 7z, WinRAR). Tanpa obfuscation, penyerang mendapatkan peta lengkap aplikasi: nama paket, kelas, metode dan field. Alat seperti jadx atau Bytecode Viewer mengembalikan hampir kode Java asli dari file DEX dalam hitungan detik. Obfuscation tidak membuat kode tidak rentan, namun secara signifikan meningkatkan ambang masuk: alih-alih nama yang bermakna, pembaca melihat a(), b(), c().
Tujuan umum obfuscation: melindungi logika komersial (algoritma, rumus perhitungan), mempersulit pencurian kunci API dan token, mencegah penggantian kelas melalui reflection, melindungi dari patching dan modifikasi APK (repackage attack). Dalam praktiknya 70% tugas diselesaikan dengan penggantian nama — itulah mengapa ProGuard / R8 dijalankan.
Di bawah ini adalah file proguard-rules.pro tipikal untuk proyek Android dengan Retrofit, Gson dan Parcelable. Aturan -keep menyimpan kelas dan metode yang diperlukan untuk pengoperasian pustaka melalui reflection. Tanpa aturan ini, R8 akan menghapus atau mengganti nama kelas yang diakses oleh pustaka berdasarkan nama string.
# =====================
# Retrofit — menyimpan antarmuka
# =====================
-keep,allowobfuscation,allowshrinking interface retrofit2.** { *; }
-keepattributes Signature, Exceptions
# =====================
# Gson — serialisasi 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 — menghapus log dari 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(...);
}
# =====================
# Kelas data Kotlin — menyimpan konstruktor
# =====================
-keepclassmembers class * {
@kotlin.Metadata <fields>;
}
# =====================
# Activity — titik masuk
# =====================
-keep class * extends android.app.Activity {
@android.annotation.SuppressLint <methods>;
}Setiap direktif dalam file .pro menyelesaikan tugas tertentu. -keep mencegah penghapusan atau penggantian nama seluruh kelas. -keepclassmembers hanya melindungi anggota kelas (field dan metode), namun mengizinkan penghapusan kelas itu sendiri jika tidak digunakan. -assumenosideeffects memberi tahu R8 bahwa pemanggilan metode tidak memiliki efek samping dan dapat dihapus dengan aman. Direktif -keepattributes menyimpan metadata dalam bytecode — anotasi, tanda tangan, pengecualian.
Aturan -keep,allowobfuscation,allowshrinking untuk Retrofit mengizinkan R8 mengganti nama antarmuka, tetapi tidak menghapusnya. Ini diperlukan karena Retrofit mengakses antarmuka melalui proxy dinamis (java.lang.reflect.Proxy), dan penghapusan akan menyebabkan ClassNotFoundException saat runtime. Demikian pula, Gson menggunakan reflection untuk mengakses field dengan anotasi @SerializedName — tanpa -keepclassmembers field akan dihapus sebagai tidak digunakan.
Minifikasi (shrinking) — proses penghapusan kode dan sumber daya yang tidak digunakan dari build akhir. ProGuard dan R8 menganalisis graf panggilan, mulai dari titik masuk (Activity, Service, BroadcastReceiver), dan menghapus kelas serta metode yang tidak dapat dijangkau melalui rantai panggilan. ShrinkResources — tahap tambahan yang menghapus sumber daya yang tidak digunakan dari res/ (layout, drawable, string, color).
Minifikasi memberikan keuntungan terbesar pada proyek besar dengan pustaka. Gambaran tipikal: proyek menggunakan 10% kode dari pustaka yang terhubung (misalnya Google Play Services). Tanpa minifikasi, seluruh kode pustaka masuk ke APK. Dengan minifikasi, R8 menghapus 70–90% kode pustaka, hanya menyisakan kelas dan metode yang benar-benar digunakan. Ini secara langsung mempengaruhi ukuran APK, waktu muat dan konsumsi memori.
Mekanisme ShrinkResources bekerja bersama dengan minifikasi kode. Setelah R8 menentukan kelas mana yang digunakan, penyusutan sumber daya menganalisis referensi ke sumber daya dari kode: R.layout.main, R.drawable.icon, getString(R.string.title). Semua sumber daya yang tidak memiliki referensi langsung atau tidak langsung dihapus dari APK atau AAB akhir. Untuk ini digunakan file sumber daya resources.arsc dan folder res/.
Nuansa penting: sumber daya dapat dipanggil melalui getIdentifier() atau Resources.getResourceName() berdasarkan nama string, melewati kelas R. Dalam kasus seperti itu, R8 tidak melihat hubungan langsung dan dapat menghapus sumber daya yang sebenarnya digunakan. Untuk melindungi sumber daya tersebut ada direktif -keep class **.R$* { *; } — ini menyimpan semua pengenal kelas R.
<!-- Contoh: sumber daya yang hanya digunakan melalui getIdentifier() -->
<string name="dynamic_title_welcome">Selamat datang</string>
<string name="dynamic_title_share">Bagikan</string>
<!-- Kode Kotlin yang mengakses berdasarkan string -->
<!-- val title = getString(resources.getIdentifier( -->
<!-- \"dynamic_title_${type}\", \"string\", packageName)) -->Dalam kasus ini R8 tidak melihat referensi statis ke dynamic_title_welcome di kelas R, karena akses dilakukan melalui getIdentifier dengan nama dinamis. Untuk menyimpan sumber daya tersebut, perlu ditambahkan dalam proguard-rules.pro direktif -keepclassmembers class **.R$string { *; } — ini melarang penghapusan field apapun dari semua kelas R$string.
| Direktif | Tujuan | Contoh |
|---|---|---|
| -keep | Menyimpan kelas dan semua anggotanya | -keep class com.example.api.** { *; } |
| -keepclassmembers | Hanya menyimpan anggota kelas | -keepclassmembers class * { @SerializedName <fields>; } |
| -keepattributes | Menyimpan metadata bytecode | -keepattributes *Annotation*, Signature |
| -assumenosideeffects | Menghapus pemanggilan tanpa efek samping | -assumenosideeffects class Log { d(...); } |
| -dontwarn | Menekan peringatan | -dontwarn com.example.legacy.** |
Meskipun R8 adalah penerus ProGuard, ada perbedaan prinsip dalam arsitektur, kinerja dan perilaku antara alat-alat tersebut. Google secara resmi menghentikan dukungan untuk ProGuard di Android Gradle Plugin mulai AGP 7.0, namun ProGuard terus digunakan dalam proyek yang memerlukan perilaku optimalisasi khusus yang tidak tersedia di R8.
| Karakteristik | ProGuard | R8 |
|---|---|---|
| Pengembang | GuardSquare (Eric Lafourge) | |
| Tahun rilis | 2002 | 2018 (stabil pada 2019) |
| Arsitektur | 4 fase terpisah (shrink → optimize → obfuscate → preverify) | Satu proses: shrink + optimize + obfuscate bersamaan |
| Integrasi di AGP | Alat eksternal, dijalankan setelah javac | Terintegrasi dalam kompiler D8 DEX |
| Kecepatan build | 2–3 kali lebih lambat | Lebih cepat karena satu proses dan integrasi native |
| Dukungan Kotlin | Terbatas (masalah dengan inline, lambdas, coroutines) | Penuh: coroutines, fungsi inline, data class |
| File mapping | mapping.txt (kompatibel dengan retrace) | mapping.txt (format sama) |
| Kustomisasi optimalisasi | 60+ opsi -optimizationpasses, -optimizations | Terbatas: sebagian besar optimalisasi diaktifkan secara default |
| Status dukungan | Digantikan oleh R8 (AGP 7.0+ tidak menggunakan) | Pengembangan aktif, bagian dari AOSP |
R8 lebih agresif daripada ProGuard dalam menghapus kode yang dianggap mati. Ini menyebabkan situasi di mana build debug berfungsi, namun build release crash dengan ClassNotFoundException atau NoSuchMethodException. Kasus umum: pustaka yang menggunakan reflection berdasarkan nama kelas (Gson, Moshi, Retrofit, Room, Dagger); pemanggilan ServiceLoader atau java.util.ServiceLoader; proxy dinamis (java.lang.reflect.Proxy); metode native (JNI). Solusi — tambahkan -keep untuk semua kelas yang dipanggil melalui reflection.
# Masalah umum reflection — R8 tidak melihat hubungan statis
# Room — menyimpan DAO dan migrasi
-keep class * extends androidx.room.RoomDatabase { *; }
-keep class *.DatabaseMigrations { *; }
# Dagger / Hilt — menyimpan komponen
-keep class * extends dagger.hilt.android.components.** { *; }
# JNI — tidak mengganti nama metode native
-keepclasseswithmembernames class * {
native <methods>;
}
# Data Binding — menyimpan kelas Binding
-keep class *.databinding.** { *; }Jika setelah menambahkan aturan build masih crash, gunakan bendera -printconfiguration full-config.txt di proguard-rules.pro. R8 akan menghasilkan file konfigurasi lengkap yang menunjukkan aturan mana yang diterapkan dan kelas mana yang disimpan. Juga berguna direktif -whyareyoukeeping class com.example.MyClass — ini menampilkan alasan mengapa R8 memutuskan untuk menyimpan kelas tersebut.
Konfigurasi yang benar dari ProGuard rules — kunci untuk obfuscation yang stabil tanpa bug saat runtime. Di bawah ini adalah proses konfigurasi langkah demi langkah untuk proyek baru atau untuk proyek di mana obfuscation menyebabkan kesalahan.
Mulailah dengan menghubungkan file standar Android SDK — proguard-android-optimize.txt. Ini berisi aturan untuk komponen dasar Android: Activity, Service, BroadcastReceiver, ContentProvider, View, Fragment. File ini terletak di folder SDK: $ANDROID_HOME/tools/proguard/proguard-android-optimize.txt. Jika Anda menggunakan AGP, getDefaultProguardFile akan memuatnya secara otomatis.
Setiap pustaka populer memiliki aturan ProGuard yang direkomendasikan. Retrofit, OkHttp, Glide, Fresco, Coil, Room, Dagger/Hilt, Kotlin Coroutines — semuanya memerlukan aturan -keep khusus. Biasanya aturan disertakan dalam pustaka AAR dan terhubung secara otomatis melalui consumer guard rules. Periksa apakah pustaka menyediakan file proguard.txt di dalam AAR — ini adalah tanda bahwa aturan sudah diperhitungkan.
Sebelum publikasi, wajib menguji build release pada perangkat nyata atau emulator. Masalah obfuscation hanya muncul saat runtime. Periksa: otentikasi (login/registrasi), memuat data dari jaringan, navigasi antar layar, kamera dan galeri, notifikasi push, Deeplinks, WebView. Setiap crash di build release harus didekode melalui retrace dengan file mapping dan menambahkan aturan -keep yang hilang.
File mapping dihasilkan di build/outputs/mapping/release/mapping.txt. File ini wajib disimpan: tanpa itu tidak mungkin mendekode log crash dari Google Play Console. Sertakan mapping.txt dalam sistem kontrol versi atau unggah sebagai artefak CI. Google Play Console menerima file mapping secara otomatis saat mengunggah AAB dengan uploading mapping.txt diaktifkan.
Di bawah ini adalah alur kerja lengkap konfigurasi obfuscation dalam file proguard-rules.pro dengan komentar untuk setiap grup aturan.
# ===========================================
# proguard-rules.pro — contoh lengkap
# ===========================================
# --- Pengaturan umum ---
-keepattributes *Annotation*, Signature, Exceptions, InnerClasses, EnclosingMethod
-dontpreverify
# --- Komponen 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 {}
# --- Serialisasi ---
-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();
}
# --- Hanya R8: penyimpanan paksa ---
# (ProGuard mengabaikan direktif ini)
-keep,allowobfuscation class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator CREATOR;
}Setelah konfigurasi, lakukan build: ./gradlew assembleRelease. Periksa apakah di build/outputs/mapping/release/ telah muncul file: mapping.txt (korespondensi nama asli dan yang diobfusksi), seeds.txt (kelas yang disimpan oleh aturan -keep), usage.txt (kelas yang dihapus saat minifikasi). Ukuran APK setelah obfuscation harus berkurang 20–50% tergantung pada jumlah pustaka yang terhubung.
Pertanyaan yang Sering Diajukan
R8 — penerus ProGuard, dikembangkan oleh Google. R8 melakukan obfuscation, minifikasi dan optimalisasi dalam satu proses, bekerja 2–3 kali lebih cepat dari ProGuard dan terintegrasi langsung ke Android Gradle Plugin. ProGuard menggunakan empat fase terpisah dan memerlukan peluncuran eksternal. Mulai AGP 7.0 ProGuard tidak digunakan — secara default R8 yang bekerja.
Ya, R8 menggunakan ProGuard rules yang sama (file .pro). Direktif -keep, -keepclassmembers, -keepattributes, -assumenosideeffects bekerja secara identik. Aturan dasar berasal dari proguard-android-optimize.txt dari Android SDK, dan yang spesifik untuk pustaka (Retrofit, Room, Gson) ditambahkan dalam proguard-rules.pro proyek. Tanpa aturan ini, R8 dapat menghapus kelas yang diperlukan untuk pengoperasian pustaka melalui reflection.
R8 diaktifkan secara default di Android Gradle Plugin mulai AGP 3.4. Untuk mengaktifkan minifikasi, set isMinifyEnabled = true di blok release buildType file build.gradle.kts. Bendera tambahan isShrinkResources = true mengaktifkan penghapusan sumber daya yang tidak digunakan. Di gradle.properties, R8 dapat dinonaktifkan secara paksa melalui android.enableR8=false, namun ini tidak disarankan — R8 lebih cepat dan lebih stabil.
Obfuscation — penggantian nama kelas, metode dan field menjadi nama pendek tanpa makna (a, b, c). Kelas com.example.app.auth.LoginManager berubah menjadi a.a.a, metode authenticateUser menjadi a. Ini mempersulit reverse engineering aplikasi, namun tidak mempengaruhi logika eksekusi. ProGuard dan R8 hanya mengganti nama elemen yang tidak dilindungi oleh aturan -keep. File mapping menyimpan korespondensi nama asli dan yang diobfusksi untuk mendekode log crash.
Untuk mendekode stack trace digunakan utilitas retrace (bagian dari ProGuard/R8 SDK). Perintah: retrace mapping.txt crash-stacktrace.txt. File mapping terletak di build/outputs/mapping/release/mapping.txt. Google Play Console juga mendukung unggahan mapping.txt saat publikasi AAB — log crash secara otomatis didekode di konsol. Tanpa file mapping, stack trace hanya akan berisi nama yang diobfusksi a.b.c(), yang tidak berguna untuk debugging.
Kesimpulan
Kami akan mengembangkan aplikasi seluler turnkey
IT Sectr membuat aplikasi iOS dan Android untuk startup dan bisnis sejak 2017. Kami akan memberi saran dan mengusulkan solusi terbaik.
Baca juga