ProGuard/R8: ουσία, συσκότιση και προστασία εφαρμογών Android

Συγγραφέας: IT Sectr Δημοσιεύτηκε: 2026-02-14 Χρόνος ανάγνωσης: 8 λεπ

ProGuard και R8 — εργαλεία συσκότισης, σμίκρυνσης και βελτιστοποίησης για εφαρμογές Android. Το ProGuard, που δημιουργήθηκε το 2002, ήταν για μεγάλο διάστημα το de facto πρότυπο για την προστασία κώδικα Java. Το R8 — ο διάδοχός του, που αναπτύχθηκε από την Google και ενσωματώθηκε στο Android Gradle Plugin από το AGP 3.4. Και τα δύο εργαλεία μειώνουν το μέγεθος APK, αφαιρούν νεκρό κώδικα και δυσχεραίνουν τη reverse engineering. Σύμφωνα με το Android Developers, το R8 εκτελεί το build 2–3 φορές γρηγορότερα από το ProGuard με συγκρίσιμη ποιότητα συσκότισης.

Κύρια Σημεία

  • ProGuard — εργαλείο συσκότισης και βελτιστοποίησης bytecode Java, πρότυπο για Android από τη δεκαετία του 2000
  • R8 — διάδοχος του ProGuard από την Google, ενσωματωμένο στο AGP, εκτελεί συσκότιση, σμίκρυνση και βελτιστοποίηση σε ένα πέρασμα
  • Συσκότιση μετονομάζει κλάσεις και μεθόδους σε σύντομα ονόματα, δυσχεραίνοντας τη reverse engineering της εφαρμογής
  • Σμίκρυνση αφαιρεί αχρησιμοποίητες κλάσεις, μεθόδους και πεδία, μειώνοντας το μέγεθος του τελικού APK/AAB
  • ProGuard rules (αρχεία .pro) ελέγχουν ποια μέρη του κώδικα διατηρούνται, συσκοτίζονται ή αφαιρούνται

Τι είναι το ProGuard;

ProGuard — είναι ένα εργαλείο ανοιχτού κώδικα (Apache 2.0) για συσκότιση, σμίκρυνση, βελτιστοποίηση και προεπαλήθευση bytecode Java. Αναπτύχθηκε από τον Eric Lafourge το 2002 στο πλαίσιο του έργου SourceForge. Το ProGuard δέχεται ως είσοδο μεταγλωττισμένες κλάσεις Java (.class) ή αρχεία JAR και παράγει επεξεργασμένες κλάσεις ίδιας μορφής, αλλά μικρότερου μεγέθους και με μετονομασμένα στοιχεία.

Για μεγάλο χρονικό διάστημα, το ProGuard ήταν το μοναδικό πρότυπο για την προστασία εφαρμογών Android από τη reverse engineering. Η Google συνιστούσε επίσημα τη χρήση του στο Android SDK και παρείχε την προεπιλεγμένη διαμόρφωση στο αρχείο proguard-android-optimize.txt μέσα στα SDK tools. Το ProGuard λειτουργούσε ως ξεχωριστό εργαλείο, που εκτελούνταν μετά τη μεταγλώττιση κώδικα Java σε bytecode και πριν από τη συσκευασία σε DEX.

Αρχιτεκτονική ProGuard

Το ProGuard αποτελείται από τέσσερις διαδοχικές φάσεις: shrink (αφαίρεση αχρησιμοποίητων κλάσεων), optimize (βελτιστοποίηση bytecode — inlining, αφαίρεση νεκρού κώδικα), obfuscate (μετονομασία κλάσεων, μεθόδων και πεδίων σε σύντομα ονόματα), preverify (έλεγχος συμβατότητας με JVM). Κάθε φάση ελέγχεται από ξεχωριστούς κανόνες από τα αρχεία διαμόρφωσης.

Στη φάση συσκότισης, το ProGuard δημιουργεί ένα αρχείο mapping (mapping.txt) που αντιστοιχίζει τα αρχικά ονόματα με τα συσκοτισμένα. Αυτό το αρχείο είναι κρίσιμο για την αποκωδικοποίηση αρχείων καταγραφής crash από builds release μέσω του βοηθητικού προγράμματος retrace. Χωρίς αρχείο mapping, το stack trace μετατρέπεται σε ένα σύνολο γραμμάτων a(), b(), c() χωρίς δυνατότητα επαναφοράς του αρχικού περιβάλλοντος.

Φάση ProGuardΣκοπόςΑποτέλεσμα
ShrinkΑνάλυση γράφου κλήσεων και αφαίρεση νεκρού κώδικαΜείωση αριθμού κλάσεων στο APK
OptimizeInlining μεθόδων, αφαίρεση αχρησιμοποίητων παραμέτρωνΕπιτάχυνση εκτέλεσης κώδικα
ObfuscateΜετονομασία κλάσεων, πεδίων και μεθόδωνΠροστασία από reverse engineering
PreverifyΠροσθήκη χαρακτηριστικών StackMap για JVMΣυμβατότητα με Java 6+

Τι είναι το R8;

R8 — είναι το εργαλείο συσκότισης και σμίκρυνσης επόμενης γενιάς από την Google, που παρουσιάστηκε για πρώτη φορά στο Android Studio 3.3 (Νοέμβριος 2018) και έγινε το πρότυπο στο AGP 3.4 (Αύγουστος 2019). Σε αντίθεση με το ProGuard, το R8 αποτελεί μέρος του μεταγλωττιστή D8/R8 που μετατρέπει bytecode Java σε μορφή DEX. Το R8 εκτελεί όλες τις φάσεις — συσκότιση, σμίκρυνση και βελτιστοποίηση — σε ένα πέρασμα, χωρίς μεταφορά ενδιάμεσων αρχείων μεταξύ εργαλείων.

Η Google ανέπτυξε το R8 με δύο στόχους: να επιταχύνει το build (το 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"
            )
        }
    }
}

Ο κώδικας δείχνει την τυπική διαμόρφωση ενός build 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.

Γιατί χρειάζεται η συσκότιση

Τα αρχεία APK Android είναι αρχεία που μπορούν να ανοίξουν με οποιοδήποτε αρχειοθέτη (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 διατηρεί μεταδεδομένα στο bytecode — σχολιασμούς, υπογραφές, εξαιρέσεις.

Ο κανόνας -keep,allowobfuscation,allowshrinking για το Retrofit επιτρέπει στο R8 να μετονομάζει διεπαφές, αλλά όχι να τις αφαιρεί. Αυτό είναι απαραίτητο επειδή το Retrofit αποκτά πρόσβαση σε διεπαφές μέσω δυναμικού proxy (java.lang.reflect.Proxy), και η αφαίρεση θα οδηγήσει σε ClassNotFoundException κατά το runtime. Παρομοίως, το Gson χρησιμοποιεί reflection για πρόσβαση σε πεδία με σχολιασμό @SerializedName — χωρίς -keepclassmembers τα πεδία θα αφαιρεθούν ως αχρησιμοποίητα.

Σμίκρυνση και ShrinkResources

Σμίκρυνση (shrinking) — η διαδικασία αφαίρεσης αχρησιμοποίητου κώδικα και πόρων από το τελικό build. Το 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Διατηρεί μεταδεδομένα bytecode-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 (Eric Lafourge)Google
Έτος κυκλοφορίας20022018 (σταθερό 2019)
Αρχιτεκτονική4 ξεχωριστές φάσεις (shrink → optimize → obfuscate → preverify)Ένα πέρασμα: shrink + optimize + obfuscate ταυτόχρονα
Ενσωμάτωση στο AGPΕξωτερικό εργαλείο, εκτελείται μετά το javacΕνσωματωμένο στον μεταγλωττιστή D8 DEX
Ταχύτητα build2–3 φορές πιο αργόΓρηγορότερο χάρη σε ένα πέρασμα και εγγενή ενσωμάτωση
Υποστήριξη KotlinΠεριορισμένη (προβλήματα με inline, lambdas, coroutines)Πλήρης: coroutines, inline συναρτήσεις, data class
Αρχείο mappingmapping.txt (συμβατό με retrace)mapping.txt (ίδια μορφή)
Προσαρμογή βελτιστοποίησης60+ επιλογές -optimizationpasses, -optimizationsΠεριορισμένη: οι περισσότερες βελτιστοποιήσεις είναι ενεργοποιημένες από προεπιλογή
Κατάσταση υποστήριξηςΑντικαταστάθηκε από το R8 (AGP 7.0+ δεν χρησιμοποιεί)Ενεργή ανάπτυξη, μέρος του AOSP

Πότε το R8 μπορεί να σπάσει το build

Το R8 είναι πιο επιθετικό από το ProGuard στην αφαίρεση κώδικα που θεωρεί νεκρό. Αυτό οδηγεί σε καταστάσεις όπου το debug build λειτουργεί, αλλά το release καταρρέει με ClassNotFoundException ή NoSuchMethodException. Τυπικές περιπτώσεις: βιβλιοθήκες που χρησιμοποιούν reflection με όνομα κλάσης (Gson, Moshi, Retrofit, Room, Dagger); κλήσεις ServiceLoader ή java.util.ServiceLoader; δυναμικά proxy (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.** { *; }

Εάν μετά την προσθήκη κανόνων το build εξακολουθεί να καταρρέει, χρησιμοποιήστε τη σημαία -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: Δοκιμή build release

Πριν από τη δημοσίευση, δοκιμάστε υποχρεωτικά το build release σε πραγματική συσκευή ή εξομοιωτή. Τα προβλήματα συσκότισης εμφανίζονται μόνο κατά το runtime. Ελέγξτε: έλεγχο ταυτότητας (σύνδεση/εγγραφή), φόρτωση δεδομένων από το δίκτυο, πλοήγηση μεταξύ οθονών, κάμερα και γκαλερί, push ειδοποιήσεις, Deeplinks, WebView. Κάθε κατάρρευση στο build 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.

Παρακάτω είναι η πλήρης ροή εργασίας ρύθμισης συσκότισης στο αρχείο 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;
}

Μετά τη ρύθμιση, εκτελέστε το build: ./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. Αυτό δυσχεραίνει τη reverse engineering της εφαρμογής, αλλά δεν επηρεάζει τη λογική εκτέλεσης. Το ProGuard και το R8 μετονομάζουν μόνο στοιχεία που δεν προστατεύονται από κανόνες -keep. Το αρχείο mapping διατηρεί την αντιστοιχία αρχικών και συσκοτισμένων ονομάτων για αποκωδικοποίηση αρχείων καταγραφής crash.

Πώς να κάνετε debug ένα crash-log από συσκοτισμένη εφαρμογή;

Για την αποκωδικοποίηση 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 — κλασικό εργαλείο συσκότισης και βελτιστοποίησης bytecode Java, αποτελούμενο από τέσσερις διαδοχικές φάσεις
  • R8 — σύγχρονος διάδοχος από την Google, ενσωματωμένος στο AGP, εκτελεί όλες τις φάσεις σε ένα πέρασμα με 2–3 φορές υψηλότερη απόδοση
  • Συσκότιση μετονομάζει κλάσεις, μεθόδους και πεδία σε σύντομα ονόματα, δυσχεραίνοντας τη reverse engineering και προστατεύοντας την εμπορική λογική της εφαρμογής
  • Σμίκρυνση αφαιρεί αχρησιμοποίητο κώδικα και πόρους, μειώνοντας το μέγεθος APK κατά 20–50% σε τυπικά έργα
  • ProGuard rules (αρχεία .pro) ελέγχουν τη συμπεριφορά συσκότισης — οι οδηγίες -keep, -keepclassmembers, -assumenosideeffects καθορίζουν ποια στοιχεία διατηρούνται, αφαιρούνται ή μετονομάζονται
  • Αρχείο mapping (mapping.txt) — κρίσιμο τεχνούργημα build για αποκωδικοποίηση αρχείων καταγραφής crash από builds release μέσω retrace
  • Δοκιμή του build release σε πραγματική συσκευή είναι υποχρεωτική — τα προβλήματα συσκότισης εμφανίζονται μόνο κατά το runtime και απαιτούν προσθήκη ελλειπόντων κανόνων -keep

Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση

Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.

Συζήτηση έργου

Διαβάστε επίσης