StrictMode är ett inbyggt utvecklarverktyg i Android SDK som i realtid upptäcker och rapporterar slumpmässiga I/O-operationer och nätverksanrop på applikationens huvudtråd. Det åtgärdar inte fel, utan fungerar som en detektor — det kastar undantag eller skriver till LogCat när angivna policyer överträds. Enligt Google, 2024 kan korrekt konfiguration av StrictMode upptäcka upp till 80% av prestandaproblemen redan innan applikationen lanseras.
Huvudpunkter
StrictMode är ett API som ingår i Android SDK sedan API Level 9 (Android 2.3 Gingerbread). Dess uppgift är att i realtid upptäcka oavsiktlig körning av tunga operationer på huvudtråden (UI) som kan blockera renderingen av gränssnittet. Huvudtråden ansvarar för användarinmatning, layoutberäkning och rendering — varje blockering längre än 16 ms leder till att bildrutor hoppas över.
StrictMode följer principen ”fail fast” — upptäck problemet så tidigt som möjligt, helst vid första uppkomsten. Istället för att vänta på användarklagomål om långsamhet får utvecklaren en signal (loggar, dialogruta eller crash) direkt i utvecklingsfasen. Verktyget kräver inga ytterligare bibliotek eller Gradle-konfiguration — några rader kod i Application.onCreate räcker och det fungerar automatiskt på alla enheter.
StrictMode är avsett för alla Android-utvecklare, oavsett erfarenhet. Nybörjare hjälper det att forma korrekta vanor (att inte göra nätverksförfrågningar i UI-tråden), erfarna — att automatisera kvalitetskontroll i CI/CD-pipeline. Stora projekt (Google, Uber, Spotify) aktiverar StrictMode i debug builds med penaltyDeath och i release builds inaktiverar de det genom att kontrollera BuildConfig.DEBUG.
StrictMode fångar upp systemanrop som kan blockera tråden och jämför dem med uppsättningen aktiva policyer. Om anropet matchar en policy och utförs på huvudtråden tillämpar StrictMode den angivna penaltyn. Avlyssningsmekanismen är implementerad via en intra-process hook — den använder inte reflection och fungerar med minimal overhead.
När policyn aktiveras placerar StrictMode sin hanterare i systemanropens ingångspunkt (FileInputStream, FileOutputStream, Socket, URLConnection). När applikationen anropar till exempel URLConnection.openStream på huvudtråden kontrollerar StrictMode den aktuella tråden — om det är main thread aktiveras verktyget. I Android 6.0+ har mekanismen förstärkts: nätverksanrop i main thread genererar NetworkOnMainThreadException även utan StrictMode, men StrictMode möjliggör kontroll även av disk-I/O.
Varje policy kan ha sin egen penalty-typ eller kombination: penaltyLog — skrivning till LogCat med stacktrace, penaltyDialog — visning av dialogruta för användaren (endast i debug), penaltyDeath — kasta undantag och krascha applikationen, penaltyDropBox — spara data i DropBoxManager för senare analys. För CI/CD-pipeline rekommenderas penaltyDeath — det garanterar att ingen merge med överträdelse förblir obemärkt.
class App : Application() {
override fun onCreate() {
super.onCreate()
if (BuildConfig.DEBUG) {
StrictMode.setThreadPolicy(
StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectNetwork()
.penaltyLog()
.penaltyDeath()
.build()
)
}
}
}
StrictMode delar in policyerna i två nivåer: ThreadPolicy (tråd — vad man inte ska göra på huvudtråden) och VmPolicy (virtuell maskin — minnes- och resursläckor). Båda nivåerna konfigureras oberoende och fungerar parallellt.
På trådnivå kontrollerar StrictMode fyra typer av överträdelser: läsning från disk (detectDiskReads), skrivning till disk (detectDiskWrites), nätverksoperationer (detectNetwork) och anpassade långsamma anrop (detectCustomSlowCalls). disk_read utlöses vid varje läsning av SharedPreferences, SQLite, filer på huvudtråden. network — vid HTTP-förfrågningar, WebSocket, Socket-anslutningar. I Android 11+ lades detectUnbufferedIO till för detektion av obuffrad I/O.
VmPolicy kontrollerar läckor på ART virtuell maskinnivå: detectActivityLeaks (Aktiviteter som inte förstörts), detectLeakedClosableObjects (inte stängda Cursor, Stream, Socket), detectLeakedRegistrationObjects (inte avregistrerade BroadcastReceiver, ServiceConnection). Om VmPolicy upptäcker att en Aktivitet har skapats men inte förstörts efter anrop till onDestroy, skriver den ut full stack trace — det sparar timmar av felsökning av minnesläckor.
| Policy | Nivå | Vad den detekterar |
|---|---|---|
| detectDiskReads | Thread | Läsning SharedPrefs, SQLite, filer i UI-tråden |
| detectDiskWrites | Thread | Skrivning till SharedPrefs, SQLite, filer i UI-tråden |
| detectNetwork | Thread | Alla nätverksoperationer i UI-tråden |
| detectActivityLeaks | VM | Aktiviteter som överlevt onDestroy |
| detectLeakedClosableObjects | VM | Ej stängda Cursor, Stream, Socket |
Genom detectCustomSlowCalls kan du markera dina egna metoder som ”misstänkta” och få varning när ett fastställt tröskelvärde överskrids. Till exempel, om din metod loadUserProfile() normalt körs på 5 ms men i vissa fall tar 200 ms — slå in den i StrictMode.noteSlowCall("loadUserProfile"). Om varaktigheten överskrider tröskeln (standard 2000 ms) genererar StrictMode en penalty. Tröskeln konfigureras via setSlowCallDurationThreshold.
Grundkonfigurationen av StrictMode tar 10 rader kod och utförs i onCreate-metoden i en anpassad Application-klass. Huvudregel: StrictMode aktiveras endast i debug builds — i release builds saktar det ner applikationen och kan generera falska larm.
Skapa en klass som ärver Application, registrera den i AndroidManifest.xml via attributet android:name och lägg till StrictMode-konfigurationen. ThreadPolicy.Builder aktiverar alla detektorer och alla penalty-typer (förutom dialog — fungerar endast med ansluten debugger). VmPolicy.Builder lägger till detektorer för Activity- och Closable-läckor. För stora projekt (100+ skärmar) rekommenderas att konfigurera VmPolicy med penaltyDeath på Activity Leaks-detektorn — det är strikt men effektivt.
class App : Application() {
override fun onCreate() {
super.onCreate()
if (BuildConfig.DEBUG) {
StrictMode.setVmPolicy(
StrictMode.VmPolicy.Builder()
.detectActivityLeaks()
.detectLeakedClosableObjects()
.detectLeakedRegistrationObjects()
.penaltyLog()
.penaltyDeath()
.build()
)
}
}
}
För automatisk kontroll i CI/CD använd penaltyDeath — om något test överträder policyn kraschar applikationen med ett undantag. Kombinera med Android Test Orchestrator så att varje test körs i en ren process. För UI-tester (Espresso, Compose Test) skriv en anpassad TestRule som fångar StrictMode-överträdelser och omvandlar dem till assertion failure. Exempel: i @Before aktivera StrictMode, i @After kontrollera att det inte fanns några violations.
Som standard är tröskeln för customSlowCall 2000 ms, för disk_read och disk_write — utan tröskel (varje operation utlöser). Via setSlowCallDurationThreshold och setSlowIoDurationThreshold kan du ställa in egna värden i millisekunder. Om din applikation legitimt läser SharedPreferences på huvudtråden (liten konfiguration), öka tröskeln till 10–20 ms — det skär bort snabba läsningar men behåller långsamma.
StrictMode är ett kraftfullt men nyckfullt verktyg. Felaktig konfiguration leder till miljontals falska larm, vilket gör att utvecklare slutar uppmärksamma dem. Nedan finns beprövade metoder samlade från erfarenheter av stora Android-team.
Detta är en järnregel: StrictMode ska ALDRIG vara aktivt i release builds. Använd flaggan BuildConfig.DEBUG eller en anpassad buildConfigField. I release builds utför många tredjepartsbibliotek legitimt operationer på huvudtråden (SDK-initiering, cache-skrivning), och StrictMode genererar falska larm. Dessutom kommer penaltyDialog i en release build att visa en dialogruta för slutanvändaren — vilket är oacceptabelt.
För små projekt (1–10 skärmar) konfigurera penaltyLog — loggar räcker för manuell analys. För medelstora projekt (10–50 skärmar) lägg till penaltyDeath för network och customSlowCalls. För stora projekt (50+ skärmar) aktivera fullständig policyuppsättning med penaltyDeath i CI/CD och för lokal utveckling — penaltyLog. Denna gradering gör att utvecklaren inte överbelastas med falska krascher men kvaliteten kontrolleras strikt i pipelinen.
Vissa bibliotek (Firebase, Crashlytics, Adjust) utför legitimt bakgrundsoperationer som StrictMode kan felaktigt detektera. Lösningar: lägg till biblioteket i whitelist via penaltyListener, uppdatera biblioteket till en version med explicit växling till bakgrundstråd eller använd StrictMode.vmPolicy. I Android 11+ har StrictMode.OnVmViolationListener introducerats för programmerbar filtrering av överträdelser baserat på stacktrace.
// Filtrering av falska positiva via penaltyListener
StrictMode.setThreadPolicy(
StrictMode.ThreadPolicy.Builder()
.detectAll()
.penaltyListener { violation ->
val stack = violation.stackTraceToString()
if ("com.google.firebase" !in stack) {
logViolation(violation)
}
}
.build()
)
StrictMode är inte det enda kvalitetskontrollverktyget i Android-ekosystemet. För att förstå dess plats, jämför vi det med Android Lint, Android Profiler och Perfetto enligt nyckelkriterier: kontrolltid, analysdjup och automatisering.
| Kriterium | StrictMode | Android Lint | Profiler / Perfetto |
|---|---|---|---|
| Kontrolltid | Runtime (under körning) | Compile time (före start) | Runtime (post-mortem) |
| Vad den kontrollerar | Disk, nätverk, läckor | XML, kod, resurser | CPU, minne, nätverk, energi |
| Automatisering | CI/CD via penaltyDeath | Gradle task + lint-baseline | Kräver manuell analys |
| Djup | Endast UI-tråd och läckor | Statisk kodanalys | Fullständig prestandabild |
| Falska larm | Medel (beror på bibliotek) | Låg (konfigurerade regler) | Inga (verkliga mätningar) |
Den bästa strategin är att kombinera alla tre angreppssätt: Android Lint fångar uppenbara fel i kompileringsfasen (t.ex. glömd IdleHandler), StrictMode detekterar problem i runtime och Android Profiler / Perfetto används för djupgående analys när de första två verktygen inte ger svar. I verkliga projekt (Google Maps, Instagram) implementeras StrictMode under den andra utvecklingsveckan — direkt efter att grundarkitekturen satts upp.
Låt oss titta på två verkliga scenarier där StrictMode hjälper till att upptäcka och åtgärda prestandaproblem: läsning av SharedPreferences på huvudtråden och en Activity-läcka via en oregistrerad callback.
När applikationen startar kommer StrictMode med policyn detectDiskReads att upptäcka läsning av SharedPreferences på huvudtråden. Lösning: ladda konfigurationen asynkront via CoroutineScope eller cachelagra i minnet vid start. SharedPreferences läser synkront XML-filen från disken — även med en liten fil (1–2 KB) tar operationen 1–5 ms, och på billiga enheter upp till 20 ms, vilket kan leda till att bildrutor hoppas över.
// ❌ Problematisk kod — läsning av SharedPrefs i UI-tråden
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// StrictMode detectDiskReads → VIOLATION!
prefs = getSharedPreferences("config", MODE_PRIVATE)
}
}
// ✅ Korrigerad kod — läsning via Coroutine
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
loadConfigAsync()
}
}
StrictMode med VmPolicy.detectActivityLeaks kommer att upptäcka en Aktivitet som har lämnat stacken (finish anropad), men vars objekt fortfarande finns kvar i minnet på grund av en statisk referens eller en oregistrerad callback. Typiskt scenario: registrering av EventBus eller LocationListener i onResume utan anrop till unregister i onPause. VmPolicy kommer att skriva ut stack trace med angivelse av raden där referensen skapades.
// ❌ Läcka — callback inte annullerad
private var locationCallback: LocationCallback? = null
override fun onResume() {
super.onResume()
locationCallback = LocationCallback(this::onLocationUpdated)
locationManager.register(locationCallback) // StrictMode → LEAK!
}
override fun onPause() {
super.onPause()
// Glömt: locationManager.unregister(locationCallback)
}
Vanliga frågor
StrictMode lägger faktiskt till en liten overhead — varje systemanrop kontrolleras för överensstämmelse med policyerna. Påverkan på prestanda är 1–3% i debug build och saknas i release (där StrictMode är avstängt). Vid aktivering av detectAll på gamla enheter (Android 6–8) kan overheaden nå 5%, därför rekommenderas att endast konfigurera nödvändiga policyer.
Ja, StrictMode är fullt kompatibelt med Jetpack Compose. Disk- och nätverkspolicyer fungerar på ramverksnivå, oberoende av UI-ramverket. Dessutom är kritikaliteten av UI-blockeringar högre i Compose — Compose ritar om bildrutor vid 120 FPS på enheter med hög uppdateringsfrekvens, vilket gör ytterligare 5 ms för filläsning mer märkbara.
Från och med Android 8.1 (API 27) kan SharedPreferences använda minnescache — om filen redan har lästs utlöser inte upprepad läsning StrictMode. Kontrollera att du anropar getSharedPreferences för första gången (kall läsning) och att policyn detectDiskReads är aktiv. Kontrollera också att StrictMode inte är åsidosatt i ett parent-free fragment.
I JUnit-tester, använd StrictMode.allowThreadDiskReads() och StrictMode.allowThreadDiskWrites() i @Before och i @After återställ inställningarna via StrictMode.enableDefaults(). För Instrumentation-tester, använd en anpassad TestRunner med tillfällig lagring av den ursprungliga policyn. I Espresso-tester är det praktiskt att slå in StrictMode-känslig kod i IdlingResource.
StrictMode fungerar endast på Android-plattformen via Android SDK. I Kotlin Multiplatform (KMP) kan commonMain-kod inte använda StrictMode, men för androidMain kan du lägga till det som vanligt. För iOS-delen använd motsvarigheten — DispatchQueue.main.async assertion för huvudtråden.
Sammanfattning
Vi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.
Läs också