StrictMode è uno strumento per sviluppatori integrato nell'Android SDK che rileva e segnala in tempo reale operazioni di I/O accidentali e chiamate di rete sul thread principale dell'applicazione. Non corregge errori ma funge da rilevatore — lancia eccezioni o scrive in LogCat quando le politiche configurate vengono violate. Secondo Google, 2024, una corretta configurazione di StrictMode può rilevare fino all'80% dei problemi di performance prima del rilascio dell'app.
Punti chiave
StrictMode è un'API inclusa nell'Android SDK dal livello API 9 (Android 2.3 Gingerbread). Il suo compito è rilevare a runtime l'esecuzione accidentale di operazioni pesanti sul thread principale (UI) che potrebbero bloccare il rendering dell'interfaccia. Il thread principale gestisce l'elaborazione dell'input utente, il calcolo del layout e il rendering — qualsiasi blocco superiore a 16 ms provoca un frame drop.
StrictMode segue il principio “fallisci rapidamente” — rilevare il problema il prima possibile, idealmente nel momento della sua prima apparizione. Invece di aspettare le lamentele degli utenti sui rallentamenti, lo sviluppatore riceve un segnale (log, finestra di dialogo o crash) direttamente nella fase di sviluppo. Lo strumento non richiede librerie aggiuntive o configurazione di Gradle — bastano poche righe di codice in Application.onCreate e funziona automaticamente su tutti i dispositivi.
StrictMode è progettato per tutti gli sviluppatori Android, indipendentemente dall'esperienza. Per i principianti, aiuta a formare buone abitudini (non fare richieste di rete nel thread UI); per gli sviluppatori esperti, automatizza il controllo qualità nel pipeline CI/CD. I grandi progetti (Google, Uber, Spotify) includono StrictMode nei build di debug con penaltyDeath e lo disabilitano nei build di rilascio tramite il controllo BuildConfig.DEBUG.
StrictMode intercetta le chiamate di sistema che potrebbero bloccare il thread e le confronta con l'insieme delle politiche attive. Se una chiamata corrisponde a una politica e viene eseguita sul thread principale, StrictMode applica la penalità specificata. Il meccanismo di intercettazione è implementato tramite un hook intra-processo — non utilizza la reflection e opera con un overhead minimo.
Quando la politica StrictMode viene attivata, inietta il suo gestore nei punti di ingresso delle chiamate di sistema (FileInputStream, FileOutputStream, Socket, URLConnection). Quando l'applicazione chiama, ad esempio, URLConnection.openStream sul thread principale, StrictMode controlla il thread corrente — se è il thread principale, lo strumento si attiva. In Android 6.0+, il meccanismo è potenziato: le chiamate di rete sul thread principale generano NetworkOnMainThreadException anche senza StrictMode, ma StrictMode consente anche di controllare l'I/O del disco.
Ogni politica può avere il proprio tipo di penalità o combinazione: penaltyLog — scrive in LogCat con stack trace, penaltyDialog — mostra una finestra di dialogo all'utente (solo debug), penaltyDeath — lancia un'eccezione e causa il crash dell'app, penaltyDropBox — salva i dati in DropBoxManager per analisi successive. Per i pipeline CI/CD, si raccomanda penaltyDeath — garantisce che nessun merge con violazioni passi inosservato.
class App : Application() {
override fun onCreate() {
super.onCreate()
if (BuildConfig.DEBUG) {
StrictMode.setThreadPolicy(
StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectNetwork()
.penaltyLog()
.penaltyDeath()
.build()
)
}
}
}
StrictMode divide le politiche in due livelli: ThreadPolicy (livello thread — cosa non può essere fatto sul thread principale) e VmPolicy (macchina virtuale — perdite di memoria e risorse). Entrambi i livelli vengono configurati indipendentemente e funzionano in parallelo.
A livello di thread, StrictMode controlla quattro tipi di violazioni: letture disco (detectDiskReads), scritture disco (detectDiskWrites), operazioni di rete (detectNetwork) e chiamate lente personalizzate (detectCustomSlowCalls). disk_read si attiva su qualsiasi lettura di SharedPreferences, SQLite o file sul thread principale. network si attiva su richieste HTTP, WebSocket e connessioni Socket. In Android 11+, è stato aggiunto detectUnbufferedIO per rilevare I/O non bufferizzato.
VmPolicy controlla le perdite a livello della macchina virtuale ART: detectActivityLeaks (attività che non sono state distrutte), detectLeakedClosableObjects (Cursor, Stream, Socket non chiusi), detectLeakedRegistrationObjects (BroadcastReceiver, ServiceConnection non deregistrati). Se VmPolicy rileva che un'Activity è stata creata ma non distrutta dopo la chiamata a onDestroy, emette uno stack trace completo — risparmiando ore di debug delle perdite di memoria.
| Politica | Livello | Cosa rileva |
|---|---|---|
| detectDiskReads | Thread | Lettura di SharedPrefs, SQLite, file nel thread UI |
| detectDiskWrites | Thread | Scrittura in SharedPrefs, SQLite, file nel thread UI |
| detectNetwork | Thread | Qualsiasi operazione di rete nel thread UI |
| detectActivityLeaks | VM | Attività che sopravvivono a onDestroy |
| detectLeakedClosableObjects | VM | Cursor, Stream, Socket non chiusi |
Tramite detectCustomSlowCalls, puoi marcare i tuoi metodi come “sospetti” e ricevere un avviso quando viene superata una soglia specificata. Ad esempio, se il tuo metodo loadUserProfile() richiede normalmente 5 ms ma a volte 200 ms — avvolgilo in StrictMode.noteSlowCall(“loadUserProfile”). Se la durata supera la soglia (default 2000 ms), StrictMode genererà una penalità. La soglia viene configurata tramite setSlowCallDurationThreshold.
La configurazione di base di StrictMode richiede 10 righe di codice e viene eseguita nel metodo onCreate di una classe Application personalizzata. La regola principale: StrictMode viene attivato solo nei build di debug — nei build di rilascio rallenta l'app e può creare falsi positivi.
Crea una classe che estende Application, registrala in AndroidManifest.xml tramite l'attributo android:name e aggiungi la configurazione di StrictMode. ThreadPolicy.Builder include tutti i rilevatori e tutti i tipi di penalità (tranne dialog — funziona solo quando un debugger è collegato). VmPolicy.Builder aggiunge rilevatori per perdite di Activity e oggetti Closable. Per i grandi progetti (100+ schermate), si consiglia di configurare VmPolicy con penaltyDeath sul rilevatore di perdite di Activity — è severo ma efficace.
class App : Application() {
override fun onCreate() {
super.onCreate()
if (BuildConfig.DEBUG) {
StrictMode.setVmPolicy(
StrictMode.VmPolicy.Builder()
.detectActivityLeaks()
.detectLeakedClosableObjects()
.detectLeakedRegistrationObjects()
.penaltyLog()
.penaltyDeath()
.build()
)
}
}
}
Per il controllo automatico in CI/CD, utilizza penaltyDeath — se un test viola la politica, l'app si bloccherà con un'eccezione. Combina con Android Test Orchestrator in modo che ogni test venga eseguito in un processo pulito. Per i test UI (Espresso, Compose Test), scrivi una TestRule personalizzata che intercetti le violazioni di StrictMode e le trasformi in errori di asserzione. Esempio: in @Before, attiva StrictMode e in @After, verifica che non ci siano state violazioni.
Per impostazione predefinita, la soglia per customSlowCall è 2000 ms, per disk_read e disk_write — nessuna soglia (qualsiasi operazione attiva). Tramite setSlowCallDurationThreshold e setSlowIoDurationThreshold, puoi impostare i tuoi valori in millisecondi. Se la tua app legge legittimamente SharedPreferences sul thread principale (piccola configurazione), aumenta la soglia a 10–20 ms — questo filtrerà le letture veloci mantenendo quelle lente.
StrictMode è uno strumento potente ma delicato. Una configurazione errata porta a milioni di falsi positivi, facendo sì che gli sviluppatori smettano di prestarvi attenzione. Di seguito le pratiche comprovate raccolte dall'esperienza di grandi team Android.
Questa è una regola ferrea: StrictMode NON deve mai essere attivo nei build di rilascio. Utilizza il flag BuildConfig.DEBUG o un buildConfigField personalizzato. Nei build di rilascio, molte librerie di terze parti eseguono legittimamente operazioni sul thread principale (inizializzazione SDK, scrittura cache) e StrictMode creerà falsi positivi. Inoltre, penaltyDialog in un build di rilascio mostrerà una finestra di dialogo all'utente finale — il che è inaccettabile.
Per i progetti piccoli (1–10 schermate), configura penaltyLog — i log sono sufficienti per l'analisi manuale. Per i progetti medi (10–50 schermate), aggiungi penaltyDeath su rete e customSlowCalls. Per i grandi progetti (50+ schermate), attiva il set completo di politiche con penaltyDeath in CI/CD e penaltyLog per lo sviluppo locale. Questa gradazione evita di sovraccaricare gli sviluppatori con falsi crash mentre controlla rigorosamente la qualità nel pipeline.
Alcune librerie (Firebase, Crashlytics, Adjust) eseguono legittimamente operazioni in background che StrictMode potrebbe rilevare erroneamente. Soluzioni: aggiungi la libreria a una whitelist tramite penaltyListener, aggiorna la libreria a una versione con commutazione esplicita al thread in background, o utilizza StrictMode.vmPolicy. In Android 11+, è stato introdotto StrictMode.OnVmViolationListener per il filtraggio programmatico delle violazioni tramite stack trace.
// Filtraggio dei falsi positivi tramite penaltyListener
StrictMode.setThreadPolicy(
StrictMode.ThreadPolicy.Builder()
.detectAll()
.penaltyListener { violation ->
val stack = violation.stackTraceToString()
if ("com.google.firebase" !in stack) {
logViolation(violation)
}
}
.build()
)
StrictMode non è l'unico strumento di controllo qualità nell'ecosistema Android. Per capire il suo posto, confrontiamolo con Android Lint, Android Profiler e Perfetto secondo criteri chiave: tempo di verifica, profondità di analisi e automazione.
| Criterio | StrictMode | Android Lint | Profiler / Perfetto |
|---|---|---|---|
| Tempo di verifica | Runtime (mentre l'app è in esecuzione) | Tempo di compilazione (prima del lancio) | Runtime (post-mortem) |
| Cosa verifica | Disco, rete, perdite | XML, codice, risorse | CPU, memoria, rete, energia |
| Automazione | CI/CD tramite penaltyDeath | Attività Gradle + lint-baseline | Richiede analisi manuale |
| Profondità | Solo thread UI e perdite | Analisi statica del codice | Quadro completo delle performance |
| Falsi positivi | Medi (dipendono dalle librerie) | Bassi (regole configurate) | Nessuno (misurazioni effettive) |
La strategia migliore è combinare tutti e tre gli approcci: Android Lint cattura errori evidenti in fase di compilazione (ad esempio, un IdleHandler dimenticato), StrictMode rileva i problemi a runtime e Android Profiler / Perfetto viene utilizzato per analisi approfondite quando i primi due strumenti non forniscono risposte. Nei progetti reali (Google Maps, Instagram), StrictMode viene introdotto nella seconda settimana di sviluppo — subito dopo aver impostato l'architettura di base.
Esaminiamo due scenari reali in cui StrictMode aiuta a rilevare e correggere problemi di performance: lettura di SharedPreferences sul thread principale e perdita di Activity tramite un callback non deregistrato.
All'avvio dell'app, StrictMode con la politica detectDiskReads rileverà la lettura di SharedPreferences sul thread principale. Soluzione: caricare la configurazione in modo asincrono tramite CoroutineScope o memorizzarla nella cache all'avvio. SharedPreferences legge in modo sincrono un file XML dal disco — anche con un file piccolo (1–2 KB), l'operazione richiede 1–5 ms e fino a 20 ms su dispositivi economici, il che può causare frame drop.
// ❌ Codice problematico — lettura di SharedPrefs nel thread UI
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// StrictMode detectDiskReads → VIOLAZIONE!
prefs = getSharedPreferences("config", MODE_PRIVATE)
}
}
// ✅ Codice corretto — lettura tramite Coroutine
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
loadConfigAsync()
}
}
StrictMode con VmPolicy.detectActivityLeaks rileverà un'Activity che è uscita dallo stack (finish è stato chiamato) ma l'oggetto Activity rimane in memoria a causa di un riferimento statico o di un callback non deregistrato. Scenario tipico: registrazione di EventBus o LocationListener in onResume senza chiamare unregister in onPause. VmPolicy emetterà uno stack trace indicando la riga in cui è stato creato il riferimento.
// ❌ Perdita — callback non cancellato
private var locationCallback: LocationCallback? = null
override fun onResume() {
super.onResume()
locationCallback = LocationCallback(this::onLocationUpdated)
locationManager.register(locationCallback) // StrictMode → PERDITA!
}
override fun onPause() {
super.onPause()
// Dimenticato: locationManager.unregister(locationCallback)
}
Domande frequenti
StrictMode aggiunge effettivamente un piccolo overhead — ogni chiamata di sistema viene verificata rispetto alle politiche. L'impatto sulle performance è dell'1–3% nei build di debug ed è assente nei build di rilascio (dove StrictMode è disabilitato). Attivando detectAll su dispositivi vecchi (Android 6–8), l'overhead può raggiungere il 5%, quindi si consiglia di configurare solo le politiche necessarie.
Sì, StrictMode è completamente compatibile con Jetpack Compose. Le politiche di disco e rete funzionano a livello del framework, indipendentemente dal framework UI. Inoltre, in Compose la criticità dei blocchi UI è maggiore — Compose ridisegna i frame a 120 FPS su dispositivi con alto refresh rate, quindi 5 ms aggiuntivi nella lettura del file diventano più evidenti.
A partire da Android 8.1 (API 27), SharedPreferences può utilizzare la cache in memoria — se il file è già stato letto, una rilettura non attiverà StrictMode. Assicurati di chiamare getSharedPreferences per la prima volta (lettura a freddo) e che la politica detectDiskReads sia attiva. Verifica anche che StrictMode non sia stato sovrascritto in un fragment senza genitore.
Nei test JUnit, utilizza StrictMode.allowThreadDiskReads() e StrictMode.allowThreadDiskWrites() in @Before e ripristina le impostazioni in @After tramite StrictMode.enableDefaults(). Per i test di strumentazione, utilizza un TestRunner personalizzato con conservazione temporanea della politica originale. Nei test Espresso, è conveniente avvolgere il codice sensibile a StrictMode in un IdlingResource.
StrictMode funziona solo sulla piattaforma Android tramite Android SDK. In Kotlin Multiplatform (KMP), il codice commonMain non può utilizzare StrictMode, ma per androidMain puoi aggiungerlo come al solito. Per la parte iOS, utilizza un analogo — l'asserzione DispatchQueue.main.async per il thread principale.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche