StrictMode είναι ένα ενσωματωμένο εργαλείο προγραμματιστή στο Android SDK που εντοπίζει σε πραγματικό χρόνο και αναφέρει τυχαίες λειτουργίες εισόδου/εξόδου και κλήσεις δικτύου στο κύριο νήμα της εφαρμογής. Δεν διορθώνει σφάλματα, αλλά λειτουργεί ως ανιχνευτής — εκτοξεύει εξαιρέσεις ή γράφει στο LogCat όταν παραβιάζονται οι καθορισμένες πολιτικές. Σύμφωνα με τα δεδομένα του Google, 2024, η σωστή ρύθμιση του StrictMode επιτρέπει τον εντοπισμό έως και 80% των προβλημάτων απόδοσης πριν από τη δημοσίευση της εφαρμογής.
Κύρια σημεία
StrictMode είναι ένα API που αποτελεί μέρος του Android SDK από το API Level 9 (Android 2.3 Gingerbread). Ο σκοπός του είναι να εντοπίζει σε χρόνο εκτέλεσης την τυχαία εκτέλεση βαριών λειτουργιών στο κύριο (UI) νήμα, που μπορεί να μπλοκάρουν την απόδοση της διεπαφής. Το κύριο νήμα είναι υπεύθυνο για την επεξεργασία εισόδου χρήστη, τον υπολογισμό διάταξης και την απόδοση — οποιοδήποτε μπλοκάρισμα μεγαλύτερο από 16 ms οδηγεί σε απώλεια καρέ.
StrictMode ακολουθεί την αρχή "fail fast" — να εντοπίζει το πρόβλημα όσο το δυνατόν νωρίτερα, ιδανικά τη στιγμή της πρώτης εμφάνισής του. Αντί να περιμένει παράπονα χρηστών για καθυστερήσεις, ο προγραμματιστής λαμβάνει ένα σήμα (logs, διάλογο ή crash) απευθείας στο στάδιο ανάπτυξης. Το εργαλείο δεν απαιτεί πρόσθετες βιβλιοθήκες ή διαμόρφωση Gradle — αρκούν μερικές γραμμές κώδικα στο Application.onCreate και λειτουργεί αυτόματα σε όλες τις συσκευές.
Το StrictMode προορίζεται για όλους τους προγραμματιστές Android, ανεξάρτητα από εμπειρία. Στους αρχάριους βοηθά να διαμορφώσουν σωστές συνήθειες (να μην κάνουν αιτήματα δικτύου στο UI νήμα), στους έμπειρους — να αυτοματοποιήσουν τον ποιοτικό έλεγχο στο CI/CD pipeline. Μεγάλα έργα (Google, Uber, Spotify) ενεργοποιούν το StrictMode σε debug builds με penaltyDeath, ενώ στα release builds το απενεργοποιούν μέσω ελέγχου BuildConfig.DEBUG.
StrictMode παρεμποδίζει κλήσεις συστήματος που μπορούν να μπλοκάρουν το νήμα και τις συγκρίνει με το σύνολο των ενεργών πολιτικών. Εάν η κλήση αντιστοιχεί σε πολιτική και εκτελείται στο κύριο νήμα, το StrictMode εφαρμόζει την καθορισμένη ποινή (penalty). Ο μηχανισμός παρεμπόδισης υλοποιείται μέσω ενός ενδοδιεργασιακού hook — δεν χρησιμοποιεί reflection και λειτουργεί με ελάχιστο overhead.
Κατά την ενεργοποίηση μιας πολιτικής, το StrictMode εισάγει τον χειριστή του στο σημείο εισόδου κλήσεων συστήματος (FileInputStream, FileOutputStream, Socket, URLConnection). Όταν η εφαρμογή καλεί, για παράδειγμα, το URLConnection.openStream στο κύριο νήμα, το StrictMode ελέγχει το τρέχον νήμα — αν είναι main thread, το εργαλείο ενεργοποιείται. Στο Android 6.0+ ο μηχανισμός ενισχύεται: οι κλήσεις δικτύου στο main thread δημιουργούν NetworkOnMainThreadException ακόμα και χωρίς StrictMode, αλλά το StrictMode επιτρέπει τον έλεγχο και του disk I/O.
Κάθε πολιτική μπορεί να έχει τον δικό της τύπο ποινής ή συνδυασμό: penaltyLog — εγγραφή στο LogCat με stack trace, penaltyDialog — εμφάνιση διαλόγου στον χρήστη (μόνο σε debug), penaltyDeath — εκτόξευση εξαίρεσης και crash εφαρμογής, penaltyDropBox — αποθήκευση δεδομένων στο DropBoxManager για μεταγενέστερη ανάλυση. Για CI/CD pipeline συνιστάται penaltyDeath — αυτό εγγυάται ότι καμία συγχώνευση με παραβίαση δεν θα περάσει απαρατήρητη.
class App : Application() {
override fun onCreate() {
super.onCreate()
if (BuildConfig.DEBUG) {
StrictMode.setThreadPolicy(
StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectNetwork()
.penaltyLog()
.penaltyDeath()
.build()
)
}
}
}
Το StrictMode χωρίζει τις πολιτικές σε δύο επίπεδα: ThreadPolicy (πολιτικές νήματος — τι δεν επιτρέπεται στο κύριο νήμα) και VmPolicy (πολιτικές εικονικής μηχανής — διαρροές μνήμης και πόρων). Και τα δύο επίπεδα ρυθμίζονται ανεξάρτητα και λειτουργούν παράλληλα.
Σε επίπεδο νήματος, το StrictMode ελέγχει τέσσερις τύπους παραβιάσεων: ανάγνωση από δίσκο (detectDiskReads), εγγραφή σε δίσκο (detectDiskWrites), λειτουργίες δικτύου (detectNetwork) και προσαρμοσμένες αργές κλήσεις (detectCustomSlowCalls). Το disk_read ενεργοποιείται σε οποιαδήποτε ανάγνωση SharedPreferences, SQLite ή αρχείων στο κύριο νήμα. Το network — σε HTTP αιτήματα, WebSocket, Socket συνδέσεις. Στο Android 11+ προστέθηκε το detectUnbufferedIO για ανίχνευση μη ρυθμισμένης εισόδου/εξόδου.
Η VmPolicy ελέγχει διαρροές σε επίπεδο εικονικής μηχανής ART: detectActivityLeaks (Activities που δεν καταστράφηκαν), detectLeakedClosableObjects (μη κλεισμένα Cursor, Stream, Socket), detectLeakedRegistrationObjects (μη ακυρωμένα BroadcastReceiver, ServiceConnection). Εάν η VmPolicy ανιχνεύσει ότι μια Activity δημιουργήθηκε αλλά δεν καταστράφηκε μετά την κλήση onDestroy, εμφανίζει πλήρες stack trace — αυτό εξοικονομεί ώρες εντοπισμού διαρροών μνήμης.
| Πολιτική | Επίπεδο | Τι ανιχνεύει |
|---|---|---|
| detectDiskReads | Thread | Ανάγνωση SharedPrefs, SQLite, αρχείων στο UI νήμα |
| detectDiskWrites | Thread | Εγγραφή σε SharedPrefs, SQLite, αρχεία στο UI νήμα |
| detectNetwork | Thread | Οποιεσδήποτε λειτουργίες δικτύου στο UI νήμα |
| detectActivityLeaks | VM | Activities που επιβίωσαν του onDestroy |
| detectLeakedClosableObjects | VM | Μη κλεισμένα Cursor, Stream, Socket |
Μέσω του detectCustomSlowCalls μπορείτε να επισημάνετε δικές σας μεθόδους ως "ύποπτες" και να λαμβάνετε προειδοποίηση όταν ξεπερνούν ένα καθορισμένο όριο. Για παράδειγμα, αν η μέθοδός σας loadUserProfile() συνήθως εκτελείται σε 5 ms αλλά σε ορισμένες περιπτώσεις διαρκεί 200 ms — τυλίξτε την σε StrictMode.noteSlowCall("loadUserProfile"). Εάν η διάρκεια υπερβαίνει το όριο (από προεπιλογή 2000 ms), το StrictMode δημιουργεί ποινή. Το όριο ρυθμίζεται μέσω του setSlowCallDurationThreshold.
Η βασική ρύθμιση του StrictMode απαιτεί 10 γραμμές κώδικα και γίνεται στη μέθοδο onCreate μιας προσαρμοσμένης κλάσης Application. Βασικός κανόνας: το StrictMode ενεργοποιείται μόνο σε debug builds — στα release builds επιβραδύνει την εφαρμογή και μπορεί να δημιουργήσει ψευδείς συναγερμούς.
Δημιουργήστε μια κλάση που κληρονομεί την Application, καταχωρήστε την στο AndroidManifest.xml μέσω του χαρακτηριστικού android:name και προσθέστε τη διαμόρφωση StrictMode. Το ThreadPolicy.Builder περιλαμβάνει όλους τους ανιχνευτές και όλους τους τύπους ποινής (εκτός από dialog — λειτουργεί μόνο όταν είναι συνδεδεμένος ο εντοπιστής σφαλμάτων). Το VmPolicy.Builder προσθέτει ανιχνευτές διαρροών Activity και Closable αντικειμένων. Για μεγάλα έργα (100+ οθόνες) συνιστάται η ρύθμιση VmPolicy με penaltyDeath στον ανιχνευτή Activity Leaks — είναι αυστηρό αλλά αποτελεσματικό.
class App : Application() {
override fun onCreate() {
super.onCreate()
if (BuildConfig.DEBUG) {
StrictMode.setVmPolicy(
StrictMode.VmPolicy.Builder()
.detectActivityLeaks()
.detectLeakedClosableObjects()
.detectLeakedRegistrationObjects()
.penaltyLog()
.penaltyDeath()
.build()
)
}
}
}
Για αυτόματο έλεγχο σε CI/CD χρησιμοποιήστε penaltyDeath — αν κάποιο τεστ παραβιάσει την πολιτική, η εφαρμογή θα καταρρεύσει με εξαίρεση. Συνδυάστε το με Android Test Orchestrator, ώστε κάθε τεστ να εκτελείται σε καθαρή διεργασία. Για UI τεστ (Espresso, Compose Test) γράψτε ένα προσαρμοσμένο TestRule που παρεμποδίζει παραβιάσεις StrictMode και τις μετατρέπει σε αποτυχία ισχυρισμού. Παράδειγμα: στο @Before ενεργοποιήστε StrictMode, και στο @After ελέγξτε ότι δεν υπήρξαν παραβιάσεις.
Από προεπιλογή, το όριο για customSlowCall είναι 2000 ms, για disk_read και disk_write — χωρίς όριο (οποιαδήποτε λειτουργία ενεργοποιείται). Μέσω των setSlowCallDurationThreshold και setSlowIoDurationThreshold μπορείτε να ορίσετε δικές σας τιμές σε χιλιοστά του δευτερολέπτου. Αν η εφαρμογή σας διαβάζει νόμιμα SharedPreferences στο κύριο νήμα (μικρή ρύθμιση), αυξήστε το όριο στα 10–20 ms — αυτό θα αποκόψει τις γρήγορες αναγνώσεις αλλά θα διατηρήσει τις αργές.
StrictMode είναι ένα ισχυρό αλλά ιδιότροπο εργαλείο. Η λανθασμένη ρύθμιση οδηγεί σε εκατομμύρια ψευδείς συναγερμούς, με αποτέλεσμα οι προγραμματιστές να σταματούν να τους δίνουν σημασία. Παρακάτω — δοκιμασμένες πρακτικές από την εμπειρία μεγάλων Android ομάδων.
Αυτός είναι αδιαπραγμάτευτος κανόνας: το StrictMode δεν πρέπει ΠΟΤΕ να είναι ενεργό σε release builds. Χρησιμοποιήστε τη σημαία BuildConfig.DEBUG ή μια προσαρμοσμένη buildConfigField. Στα release builds, πολλές βιβλιοθήκες τρίτων εκτελούν νόμιμα λειτουργίες στο κύριο νήμα (αρχικοποίηση SDK, εγγραφή cache) και το StrictMode θα δημιουργήσει ψευδείς συναγερμούς. Επιπλέον, το penaltyDialog σε release build θα εμφανίσει διάλογο στον τελικό χρήστη — κάτι που είναι απαράδεκτο.
Για μικρά έργα (1–10 οθόνες) ρυθμίστε penaltyLog — αρκούν τα logs για χειροκίνητη ανάλυση. Για μεσαία έργα (10–50 οθόνες) προσθέστε penaltyDeath σε network και customSlowCalls. Για μεγάλα έργα (50+ οθόνες) ενεργοποιήστε πλήρες σύνολο πολιτικών με penaltyDeath στο CI/CD, και για τοπική ανάπτυξη — penaltyLog. Αυτή η διαβάθμιση επιτρέπει να μην υπερφορτώνεται ο προγραμματιστής με ψευδή crashes, αλλά να ελέγχεται αυστηρά η ποιότητα στο pipeline.
Ορισμένες βιβλιοθήκες (Firebase, Crashlytics, Adjust) εκτελούν νόμιμα λειτουργίες στο παρασκήνιο που το StrictMode μπορεί να ανιχνεύσει λανθασμένα. Λύσεις: προσθέστε τη βιβλιοθήκη στη whitelist μέσω penaltyListener, ενημερώστε τη βιβλιοθήκη σε έκδοση με ρητή εναλλαγή σε background thread, ή χρησιμοποιήστε StrictMode.vmPolicy. Στο Android 11+ εμφανίστηκε το StrictMode.OnVmViolationListener για προγραμματιστικό φιλτράρισμα παραβιάσεων ανά stacktrace.
// Φιλτράρισμα false positives μέσω penaltyListener
StrictMode.setThreadPolicy(
StrictMode.ThreadPolicy.Builder()
.detectAll()
.penaltyListener { violation ->
val stack = violation.stackTraceToString()
if ("com.google.firebase" !in stack) {
logViolation(violation)
}
}
.build()
)
Το StrictMode δεν είναι το μοναδικό εργαλείο ποιοτικού ελέγχου στο οικοσύστημα Android. Για να κατανοήσουμε τη θέση του, ας το συγκρίνουμε με τα Android Lint, Android Profiler και Perfetto ως προς βασικά κριτήρια: χρόνος ελέγχου, βάθος ανάλυσης και αυτοματισμός.
| Κριτήριο | StrictMode | Android Lint | Profiler / Perfetto |
|---|---|---|---|
| Χρόνος ελέγχου | Runtime (κατά τη λειτουργία της εφαρμογής) | Compile time (πριν την εκτέλεση) | Runtime (post-mortem) |
| Τι ελέγχει | Δίσκο, δίκτυο, διαρροές | XML, κώδικα, πόρους | CPU, μνήμη, δίκτυο, ενέργεια |
| Αυτοματισμός | CI/CD μέσω penaltyDeath | Gradle task + lint-baseline | Απαιτεί χειροκίνητη ανάλυση |
| Βάθος | Μόνο UI νήμα και διαρροές | Στατική ανάλυση κώδικα | Πλήρης εικόνα απόδοσης |
| Ψευδείς συναγερμοί | Μέτριοι (εξαρτώνται από βιβλιοθήκες) | Χαμηλοί (ρυθμισμένοι κανόνες) | Κανένας (πραγματικές μετρήσεις) |
Η καλύτερη στρατηγική είναι να συνδυάζετε και τις τρεις προσεγγίσεις: το Android Lint πιάνει προφανή σφάλματα στο στάδιο μεταγλώττισης (π.χ. ξεχασμένο IdleHandler), το StrictMode εντοπίζει προβλήματα σε χρόνο εκτέλεσης, και το Android Profiler / Perfetto χρησιμοποιείται για βαθιά ανάλυση όταν τα δύο πρώτα εργαλεία δεν δίνουν απάντηση. Σε πραγματικά έργα (Google Maps, Instagram) το StrictMode εφαρμόζεται τη δεύτερη εβδομάδα ανάπτυξης — αμέσως μετά τη ρύθμιση της βασικής αρχιτεκτονικής.
Ας δούμε δύο πραγματικά σενάρια όπου το StrictMode βοηθά στον εντοπισμό και την εξάλειψη προβλημάτων απόδοσης: ανάγνωση SharedPreferences στο κύριο νήμα και διαρροή Activity μέσω μη καταχωρημένου callback.
Κατά την εκκίνηση της εφαρμογής, το StrictMode με πολιτική detectDiskReads θα ανιχνεύσει την ανάγνωση SharedPreferences στο κύριο νήμα. Λύση: φορτώστε τη ρύθμιση ασύγχρονα μέσω CoroutineScope ή αποθηκεύστε την σε cache στη μνήμη κατά την εκκίνηση. Η SharedPreferences διαβάζει σύγχρονα ένα XML αρχείο από τον δίσκο — ακόμα και για μικρό αρχείο (1–2 KB) η λειτουργία διαρκεί 1–5 ms, και σε φθηνές συσκευές έως 20 ms, κάτι που μπορεί να οδηγήσει σε απώλεια καρέ.
// ❌ Προβληματικός κώδικας — ανάγνωση SharedPrefs στο UI νήμα
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// StrictMode detectDiskReads → VIOLATION!
prefs = getSharedPreferences("config", MODE_PRIVATE)
}
}
// ✅ Διορθωμένος κώδικας — ανάγνωση μέσω Coroutine
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
loadConfigAsync()
}
}
Το StrictMode με VmPolicy.detectActivityLeaks θα ανιχνεύσει μια Activity που βγήκε από τη στοίβα (κλήθηκε finish) αλλά το αντικείμενο Activity συνεχίζει να υπάρχει στη μνήμη λόγω στατικής αναφοράς ή μη καταχωρημένου callback. Τυπικό σενάριο: καταχώρηση EventBus ή LocationListener στο onResume χωρίς κλήση unregister στο onPause. Η VmPolicy θα εμφανίσει stack trace με ένδειξη της γραμμής όπου δημιουργήθηκε η αναφορά.
// ❌ Διαρροή — το callback δεν ακυρώθηκε
private var locationCallback: LocationCallback? = null
override fun onResume() {
super.onResume()
locationCallback = LocationCallback(this::onLocationUpdated)
locationManager.register(locationCallback) // StrictMode → LEAK!
}
override fun onPause() {
super.onPause()
// Ξεχάσατε: locationManager.unregister(locationCallback)
}
Συχνές ερωτήσεις
StrictMode πράγματι προσθέτει ένα μικρό overhead — κάθε κλήση συστήματος ελέγχεται για συμμόρφωση με τις πολιτικές. Η επίδραση στην απόδοση είναι 1–3% σε debug build και απουσιάζει στο release (όπου το StrictMode απενεργοποιείται). Με την ενεργοποίηση detectAll σε παλιές συσκευές (Android 6–8) το overhead μπορεί να φτάσει το 5%, γι' αυτό συνιστάται η ρύθμιση μόνο των απαραίτητων πολιτικών.
Ναι, το StrictMode είναι πλήρως συμβατό με το Jetpack Compose. Οι πολιτικές disk και network λειτουργούν σε επίπεδο framework, ανεξάρτητα από το UI framework. Επιπλέον, στο Compose η κρισιμότητα των μπλοκαρισμάτων UI είναι μεγαλύτερη — το Compose ανασχεδιάζει καρέ με συχνότητα 120 FPS σε συσκευές υψηλών επιδόσεων, επομένως τα επιπλέον 5 ms για ανάγνωση αρχείου γίνονται πιο αισθητά.
Από το Android 8.1 (API 27), η SharedPreferences μπορεί να χρησιμοποιεί προσωρινή αποθήκευση στη μνήμη — αν το αρχείο έχει ήδη διαβαστεί, η επαναλαμβανόμενη ανάγνωση δεν ενεργοποιεί το StrictMode. Ελέγξτε ότι καλείτε getSharedPreferences για πρώτη φορά (ψυχρή ανάγνωση) και ότι η πολιτική detectDiskReads είναι ενεργή. Επίσης, ελέγξτε ότι το StrictMode δεν έχει παρακαμφθεί σε ένα parent-free fragment.
Σε JUnit τεστ χρησιμοποιήστε StrictMode.allowThreadDiskReads() και StrictMode.allowThreadDiskWrites() στο @Before, και στο @After επαναφέρετε τις ρυθμίσεις μέσω StrictMode.enableDefaults(). Για Instrumentation τεστ χρησιμοποιήστε προσαρμοσμένο TestRunner με προσωρινή αποθήκευση της αρχικής πολιτικής. Σε Espresso τεστ είναι βολικό να τυλίξετε τον ευάλωτο σε StrictMode κώδικα σε IdlingResource.
Το StrictMode λειτουργεί μόνο στην πλατφόρμα Android μέσω Android SDK. Στο Kotlin Multiplatform (KMP) ο κώδικας commonMain δεν μπορεί να χρησιμοποιήσει StrictMode, αλλά για το androidMain μπορείτε να τον προσθέσετε κανονικά. Για το τμήμα iOS χρησιμοποιήστε ανάλογο — DispatchQueue.main.async assertion για το κύριο νήμα.
Συμπεράσματα
Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση
Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.
Διαβάστε επίσης