StrictMode je vestavěný vývojářský nástroj v Android SDK, který v reálném čase detekuje a hlásí náhodné I/O operace a síťová volání v hlavním vlákně aplikace. Neopravuje chyby, ale funguje jako detektor — vyhazuje výjimky nebo zapisuje do LogCat, když dojde k porušení nastavených politik. Podle údajů Google, 2024 umožňuje správné nastavení StrictMode odhalit až 80 % problémů s výkonem ještě před vydáním aplikace.
To hlavní
StrictMode je API, které je součástí Android SDK od API Level 9 (Android 2.3 Gingerbread). Jeho úkolem je v runtime odhalovat náhodné provádění těžkých operací v hlavním (UI) vlákně, které mohou zablokovat vykreslování rozhraní. Hlavní vlákno zodpovídá za zpracování uživatelského vstupu, výpočet layoutu a vykreslování — jakékoli zablokování delší než 16 ms vede k vynechání snímku.
StrictMode se řídí principem „fail fast“ — odhalit problém co nejdříve, nejlépe v okamžiku jeho prvního výskytu. Místo čekání na stížnosti uživatelů na zpomalení dostává vývojář signál (logy, dialog nebo crash) přímo ve fázi vývoje. Nástroj nevyžaduje další knihovny ani konfiguraci Gradle — stačí pár řádků kódu v Application.onCreate a funguje automaticky na všech zařízeních.
StrictMode je určen pro všechny vývojáře pro Android, bez ohledu na zkušenosti. Začátečníkům pomáhá vytvořit si správné návyky (nedělat síťové požadavky v UI vlákně), zkušeným umožňuje automatizovat kontrolu kvality v CI/CD pipeline. Velké projekty (Google, Uber, Spotify) zapínají StrictMode v debug verzích s penaltyDeath a v release verzích ho vypínají pomocí kontroly BuildConfig.DEBUG.
StrictMode zachycuje systémová volání, která mohou blokovat vlákno, a porovnává je se sadou aktivních politik. Pokud volání odpovídá politice a probíhá v hlavním vlákně, StrictMode aplikuje nastavenou penalizaci. Mechanismus zachycení je implementován přes hook uvnitř procesu — nepoužívá reflection a pracuje s minimální režií.
Při aktivaci politiky StrictMode vkládá svůj handler do vstupního bodu systémových volání (FileInputStream, FileOutputStream, Socket, URLConnection). Když aplikace zavolá například URLConnection.openStream v hlavním vlákně, StrictMode zkontroluje aktuální vlákno — pokud jde o main thread, nástroj se spustí. Od Androidu 6.0+ je mechanismus posílen: síťová volání v main thread generují NetworkOnMainThreadException i bez StrictMode, ale StrictMode umožňuje kontrolovat i disk I/O.
Každá politika může mít vlastní typ penalizace nebo jejich kombinaci: penaltyLog — zápis do LogCat se stacktrace, penaltyDialog — zobrazení dialogu uživateli (jen v debug), penaltyDeath — vyhození výjimky a crash aplikace, penaltyDropBox — uložení dat do DropBoxManager pro pozdější analýzu. Pro CI/CD pipeline se doporučuje penaltyDeath — to zaručuje, že žádný merge s porušením nezůstane bez povšimnutí.
class App : Application() {
override fun onCreate() {
super.onCreate()
if (BuildConfig.DEBUG) {
StrictMode.setThreadPolicy(
StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectNetwork()
.penaltyLog()
.penaltyDeath()
.build()
)
}
}
}
StrictMode rozděluje politiky na dvě úrovně: ThreadPolicy (vláknové — co se nesmí dělat v hlavním vlákně) a VmPolicy (virtuálního stroje — úniky paměti a zdrojů). Obě úrovně se nastavují nezávisle a fungují paralelně.
Na úrovni vlákna StrictMode kontroluje čtyři typy porušení: čtení z disku (detectDiskReads), zápis na disk (detectDiskWrites), síťové operace (detectNetwork) a uživatelská pomalá volání (detectCustomSlowCalls). disk_read se spustí při jakémkoli čtení SharedPreferences, SQLite nebo souborů v hlavním vlákně. network — při HTTP požadavcích, WebSocket, Socket připojeních. V Androidu 11+ byl přidán detectUnbufferedIO pro odhalení nebuferovaného I/O.
VmPolicy kontroluje úniky na úrovni virtuálního stroje ART: detectActivityLeaks (Activity, které nebyly zničeny), detectLeakedClosableObjects (nezavřené Cursor, Stream, Socket), detectLeakedRegistrationObjects (nezrušené BroadcastReceiver, ServiceConnection). Pokud VmPolicy zjistí, že Activity byla vytvořena, ale po volání onDestroy nebyla zničena, vypíše plný stack trace — to šetří hodiny ladění úniků paměti.
| Politika | Úroveň | Co detekuje |
|---|---|---|
| detectDiskReads | Thread | Čtení SharedPrefs, SQLite, souborů v UI vlákně |
| detectDiskWrites | Thread | Zápis do SharedPrefs, SQLite, souborů v UI vlákně |
| detectNetwork | Thread | Jakékoli síťové operace v UI vlákně |
| detectActivityLeaks | VM | Activity, které přežily onDestroy |
| detectLeakedClosableObjects | VM | Nezavřené Cursor, Stream, Socket |
Přes detectCustomSlowCalls lze vlastní metody označit jako „podezřelé“ a dostávat varování při překročení nastaveného prahu. Například pokud vaše metoda loadUserProfile() obvykle trvá 5 ms, ale v některých případech 200 ms, obalte ji do StrictMode.noteSlowCall("loadUserProfile"). Pokud doba překročí práh (ve výchozím nastavení 2000 ms), StrictMode vygeneruje penalizaci. Práh se nastavuje přes setSlowCallDurationThreshold.
Základní nastavení StrictMode zabere 10 řádků kódu a provádí se v metodě onCreate vlastní třídy Application. Hlavní pravidlo: StrictMode se zapíná pouze v debug verzích — v release verzích zpomaluje aplikaci a může vytvářet falešná spuštění.
Vytvořte třídu, která dědí z Application, zaregistrujte ji v AndroidManifest.xml přes atribut android:name a přidejte konfiguraci StrictMode. ThreadPolicy.Builder zapíná všechny detektory a všechny typy penalizace (kromě dialog — ten funguje jen při připojeném debuggeru). VmPolicy.Builder přidává detektory úniků Activity a Closable objektů. Pro velké projekty (100+ obrazovek) se doporučuje nastavit VmPolicy s penaltyDeath na detektor Activity Leaks — je to přísné, ale účinné.
class App : Application() {
override fun onCreate() {
super.onCreate()
if (BuildConfig.DEBUG) {
StrictMode.setVmPolicy(
StrictMode.VmPolicy.Builder()
.detectActivityLeaks()
.detectLeakedClosableObjects()
.detectLeakedRegistrationObjects()
.penaltyLog()
.penaltyDeath()
.build()
)
}
}
}
Pro automatickou kontrolu v CI/CD použijte penaltyDeath — pokud nějaký test poruší politiku, aplikace spadne s výjimkou. Kombinujte s Android Test Orchestrator, aby každý test běžel v čistém procesu. Pro UI testy (Espresso, Compose Test) napište vlastní TestRule, který zachycuje porušení StrictMode a přeměňuje je na selhání assertion. Příklad: v @Before zapněte StrictMode a v @After ověřte, že nedošlo k žádným violations.
Ve výchozím nastavení je práh pro customSlowCall 2000 ms, pro disk_read a disk_write není žádný (spustí se jakákoli operace). Přes setSlowCallDurationThreshold a setSlowIoDurationThreshold lze nastavit vlastní hodnoty v milisekundách. Pokud vaše aplikace legitimně čte SharedPreferences v hlavním vlákně (malá konfigurace), zvyšte práh na 10–20 ms — to odfiltruje rychlá čtení, ale ponechá pomalá.
StrictMode je výkonný, ale náladový nástroj. Nesprávné nastavení vede k milionu falešných spuštění, kvůli kterým jim vývojáři přestanou věnovat pozornost. Níže jsou osvědčené postupy nasbírané ze zkušeností velkých Android týmů.
Toto je železné pravidlo: StrictMode NIKDY nesmí být aktivní v release verzích. Použijte příznak BuildConfig.DEBUG nebo vlastní buildConfigField. V release verzích mnoho knihoven třetích stran legitimně provádí operace v hlavním vlákně (inicializace SDK, zápis cache) a StrictMode vytvoří falešná spuštění. Navíc penaltyDialog v release verzi zobrazí dialog koncovému uživateli — což je nepřípustné.
Pro malé projekty (1–10 obrazovek) nastavte penaltyLog — logy stačí pro ruční analýzu. Pro střední projekty (10–50 obrazovek) přidejte penaltyDeath na network a customSlowCalls. Pro velké projekty (50+ obrazovek) zapněte plnou sadu politik s penaltyDeath v CI/CD a pro lokální vývoj penaltyLog. Tato gradace nepřetěžuje vývojáře falešnými crashy, ale přísně kontroluje kvalitu v pipeline.
Některé knihovny (Firebase, Crashlytics, Adjust) legitimně provádějí operace na pozadí, které StrictMode může chybně detekovat. Řešení: přidejte knihovnu do whitelist přes penaltyListener, aktualizujte knihovnu na verzi s explicitním přepnutím na background thread, nebo použijte StrictMode.vmPolicy. V Androidu 11+ se objevil StrictMode.OnVmViolationListener pro programovou filtraci porušení podle stacktrace.
// Filtrace false positives přes penaltyListener
StrictMode.setThreadPolicy(
StrictMode.ThreadPolicy.Builder()
.detectAll()
.penaltyListener { violation ->
val stack = violation.stackTraceToString()
if ("com.google.firebase" !in stack) {
logViolation(violation)
}
}
.build()
)
StrictMode není jediný nástroj kontroly kvality v Android ekosystému. Abyste pochopili jeho místo, porovnejme ho s Android Lint, Android Profiler a Perfetto podle klíčových kritérií: doby kontroly, hloubky analýzy a automatizace.
| Kritérium | StrictMode | Android Lint | Profiler / Perfetto |
|---|---|---|---|
| Doba kontroly | Runtime (během běhu aplikace) | Compile time (před spuštěním) | Runtime (post-mortem) |
| Co kontroluje | Disk, síť, úniky | XML, kód, zdroje | CPU, paměť, síť, energie |
| Automatizace | CI/CD přes penaltyDeath | Gradle task + lint-baseline | Vyžaduje ruční analýzu |
| Hloubka | Jen UI vlákno a úniky | Statická analýza kódu | Úplný obraz výkonu |
| Falešná spuštění | Střední (závisí na knihovnách) | Nízká (nastavená pravidla) | Žádná (skutečná měření) |
Nejlepší strategií je kombinovat všechny tři přístupy: Android Lint chytá zjevné chyby ve fázi kompilace (například zapomenutý IdleHandler), StrictMode detekuje problémy v runtime a Android Profiler / Perfetto se používá pro hloubkovou analýzu, když první dva nástroje nedají odpověď. V reálných projektech (Google Maps, Instagram) se StrictMode zavádí ve druhém týdnu vývoje — hned po nastavení základní architektury.
Podíváme se na dva reálné scénáře, kde StrictMode pomáhá odhalit a odstranit problémy s výkonem: čtení SharedPreferences v hlavním vlákně a únik Activity přes neregistrovaný callback.
Při startu aplikace StrictMode s politikou detectDiskReads odhalí čtení SharedPreferences v hlavním vlákně. Řešení: načítat konfiguraci asynchronně přes CoroutineScope nebo ji při startu cachovat do paměti. SharedPreferences synchronně čte XML soubor z disku — i při malém souboru (1–2 KB) operace trvá 1–5 ms, na levných zařízeních až 20 ms, což může vést k vynechání snímku.
// ❌ Problémový kód — čtení SharedPrefs v UI vlákně
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// StrictMode detectDiskReads → VIOLATION!
prefs = getSharedPreferences("config", MODE_PRIVATE)
}
}
// ✅ Opravený kód — čtení přes Coroutine
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
loadConfigAsync()
}
}
StrictMode s VmPolicy.detectActivityLeaks odhalí Activity, která opustila zásobník (byl zavolán finish), ale objekt Activity nadále visí v paměti kvůli statické referenci nebo neregistrovanému callbacku. Typický scénář: registrace EventBus nebo LocationListener v onResume bez volání unregister v onPause. VmPolicy vypíše stacktrace s uvedením řádku, kde byla reference vytvořena.
// ❌ Únik — callback nebyl zrušen
private var locationCallback: LocationCallback? = null
override fun onResume() {
super.onResume()
locationCallback = LocationCallback(this::onLocationUpdated)
locationManager.register(locationCallback) // StrictMode → LEAK!
}
override fun onPause() {
super.onPause()
// Zapomněli jste: locationManager.unregister(locationCallback)
}
Často kladené dotazy
StrictMode skutečně přidává mírnou režii — každé systémové volání se kontroluje na soulad s politikami. Dopad na výkon je 1–3 % v debug verzi a v release chybí (kde je StrictMode vypnut). Při zapnutí detectAll na starších zařízeních (Android 6–8) může režie dosáhnout 5 %, proto se doporučuje nastavit pouze potřebné politiky.
Ano, StrictMode je plně kompatibilní s Jetpack Compose. Politiky disk a network fungují na úrovni frameworku, nezávisle na UI frameworku. Navíc v Compose je kritičnost UI blokování vyšší — Compose překresluje snímky frekvencí 120 FPS na zařízeních s vysokou obnovovací frekvencí, proto je 5 ms navíc na čtení souboru znatelnější.
Od Androidu 8.1 (API 27) může SharedPreferences používat cache v paměti — pokud byl soubor již přečten, opakované čtení nespustí StrictMode. Zkontrolujte, že getSharedPreferences voláte poprvé (studené čtení) a že je aktivní politika detectDiskReads. Zkontrolujte také, že StrictMode není přepsán v parent-free fragmentu.
V JUnit testech použijte StrictMode.allowThreadDiskReads() a StrictMode.allowThreadDiskWrites() v @Before a v @After vraťte nastavení přes StrictMode.enableDefaults(). Pro Instrumentation testy použijte vlastní TestRunner s dočasným uložením původní politiky. V Espresso testech je vhodné kód citlivý na StrictMode obalit do IdlingResource.
StrictMode funguje pouze na platformě Android přes Android SDK. V Kotlin Multiplatform (KMP) nemůže kód commonMain používat StrictMode, ale pro androidMain ho můžete přidat jako obvykle. Pro iOS část použijte analog — DispatchQueue.main.async assertion pro hlavní vlákno.
Shrnutí
Vyvineme mobilní aplikaci na klíč
IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.
Přečtěte si také