StrictMode egy beépített fejlesztői eszköz az Android SDK-ban, amely valós időben észleli és jelenti a fő szálon végzett véletlen I/O-műveleteket és hálózati hívásokat. Nem javítja a hibákat, hanem detektorként működik — kivételt dob vagy a LogCat-be ír, ha egy beállított szabályzatot megsértenek. A Google, 2024 adatai szerint a StrictMode helyes beállításával a teljesítményproblémák akár 80%-a már az alkalmazás megjelenése előtt felderíthető.
A legfontosabb
StrictMode egy API, amely az API Level 9 (Android 2.3 Gingerbread) óta része az Android SDK-nak. Feladata, hogy futásidőben felderítse a fő (UI) szálon végrehajtott, nem szándékos nehéz műveleteket, amelyek blokkolhatják a felület megjelenítését. A fő szál felelős a felhasználói bevitel feldolgozásáért, a layout kiszámításáért és a megjelenítésért — minden 16 ms-nál hosszabb blokkolás kimaradt képkockát (frame drop) eredményez.
StrictMode a „fail fast” elvet követi — minél korábban, lehetőleg az első megjelenéskor fedezd fel a problémát. Ahelyett, hogy a felhasználók lassulásra vonatkozó panaszára várna, a fejlesztő jelet kap (naplók, párbeszédablak vagy crash) már a fejlesztés során. Az eszköz nem igényel extra könyvtárakat vagy Gradle-konfigurációt — néhány sornyi kód a Application.onCreate-ban elég, és minden eszközön automatikusan működik.
A StrictMode minden Android-fejlesztőnek szól, tapasztalattól függetlenül. A kezdőknek segít jó szokásokat kialakítani (ne küldj hálózati kéréseket az UI-szálon), a tapasztaltaknak automatizálja a minőségellenőrzést a CI/CD pipeline-ban. A nagy projektek (Google, Uber, Spotify) penaltyDeath-dzsel kapcsolják be a StrictMode-ot a debug-verziókban, a release-verziókban pedig a BuildConfig.DEBUG ellenőrzésével kapcsolják ki.
StrictMode elkapja azokat a rendszerhívásokat, amelyek blokkolhatják a szálat, és összehasonlítja őket az aktív szabályzatok halmazával. Ha egy hívás megfelel egy szabályzatnak, és a fő szálon fut, a StrictMode alkalmazza a beállított büntetést (penalty). Az elkapási mechanizmus folyamaton belüli hook-on keresztül valósul meg — nem használ reflectiont, és minimális overheaddel működik.
Egy szabályzat aktiválásakor a StrictMode a saját kezelőjét illeszti be a rendszerhívások belépési pontjába (FileInputStream, FileOutputStream, Socket, URLConnection). Amikor az alkalmazás például URLConnection.openStream-et hív a fő szálon, a StrictMode ellenőrzi az aktuális szálat — ha ez a main thread, az eszköz bekapcsol. Android 6.0+ rendszeren a mechanizmus megerősített: a main thread-en történő hálózati hívások StrictMode nélkül is NetworkOnMainThreadException-t generálnak, de a StrictMode a disk I/O felett is ellenőrzést biztosít.
Minden szabályzatnak lehet saját penalty-típusa vagy kombinációja: penaltyLog — írás a LogCat-be stacktrace-szel, penaltyDialog — párbeszédablak megjelenítése a felhasználónak (csak debug-ban), penaltyDeath — kivétel dobása és az alkalmazás összeomlása, penaltyDropBox — adatok mentése a DropBoxManagerbe későbbi elemzéshez. A CI/CD pipeline-hoz a penaltyDeath ajánlott — ez garantálja, hogy egyetlen, szabályzatot sértő merge sem marad észrevétlen.
class App : Application() {
override fun onCreate() {
super.onCreate()
if (BuildConfig.DEBUG) {
StrictMode.setThreadPolicy(
StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectNetwork()
.penaltyLog()
.penaltyDeath()
.build()
)
}
}
}
A StrictMode két szintre osztja a szabályzatokat: ThreadPolicy (szálszintű — mit nem szabad a fő szálon csinálni) és VmPolicy (virtuális gép szintű — memória- és erőforrás-szivárgások). Mindkét szint egymástól függetlenül állítható be, és párhuzamosan működik.
Szálszinten a StrictMode négyféle sértést ellenőriz: lemezről olvasás (detectDiskReads), lemezre írás (detectDiskWrites), hálózati műveletek (detectNetwork) és egyéni lassú hívások (detectCustomSlowCalls). A disk_read minden SharedPreferences, SQLite vagy fájl olvasásakor aktiválódik a fő szálon. A network — HTTP-kérések, WebSocket, Socket-kapcsolatok esetén. Android 11+-ban megjelent a detectUnbufferedIO a puffer nélküli I/O felderítésére.
A VmPolicy az ART virtuális gép szintjén ellenőrzi a szivárgásokat: detectActivityLeaks (nem megsemmisített Activity-k), detectLeakedClosableObjects (le nem zárt Cursor, Stream, Socket), detectLeakedRegistrationObjects (le nem regisztrált BroadcastReceiver, ServiceConnection). Ha a VmPolicy azt észleli, hogy egy Activity létrejött, de az onDestroy hívása után nem semmisült meg, teljes stack trace-t ad ki — ez órákat spórol meg a memóriaszivárgások hibakeresésében.
| Szabályzat | Szint | Mit észlel |
|---|---|---|
| detectDiskReads | Thread | SharedPrefs, SQLite, fájlok olvasása az UI-szálon |
| detectDiskWrites | Thread | Írás SharedPrefs-be, SQLite-ba, fájlokba az UI-szálon |
| detectNetwork | Thread | Bármilyen hálózati művelet az UI-szálon |
| detectActivityLeaks | VM | Az onDestroy-t túlélő Activity-k |
| detectLeakedClosableObjects | VM | Le nem zárt Cursor, Stream, Socket |
A detectCustomSlowCalls segítségével saját metódusokat jelölhetsz meg „gyanús”-ként, és figyelmeztetést kapsz, ha túllépik a beállított küszöbértéket. Például, ha a loadUserProfile() metódusod általában 5 ms-ig fut, de néha 200 ms-ig, csavard be a StrictMode.noteSlowCall("loadUserProfile") hívásba. Ha az időtartam meghaladja a küszöböt (alapértelmezés szerint 2000 ms), a StrictMode penalty-t generál. A küszöb a setSlowCallDurationThreshold beállítással módosítható.
A StrictMode alapszintű beállítása 10 sornyi kódot igényel, és az egyéni Application-osztály onCreate metódusában történik. A fő szabály: a StrictMode-ot csak debug-verziókban kapcsold be — release-verziókban lelassítja az alkalmazást, és hamis riasztásokat okozhat.
Hozz létre egy Application-ből származó osztályt, regisztráld az AndroidManifest.xml-ben az android:name attribútummal, és add hozzá a StrictMode-konfigurációt. A ThreadPolicy.Builder bekapcsolja az összes detektort és az összes penalty-típust (kivéve a dialog-ot — az csak akkor működik, ha debugger csatlakozik). A VmPolicy.Builder hozzáadja az Activity- és Closable-szivárgás-detektorokat. Nagy projektekhez (100+ képernyő) ajánlott a VmPolicy beállítása penaltyDeath-dzsel az Activity Leaks detektorra — ez szigorú, de hatékony.
class App : Application() {
override fun onCreate() {
super.onCreate()
if (BuildConfig.DEBUG) {
StrictMode.setVmPolicy(
StrictMode.VmPolicy.Builder()
.detectActivityLeaks()
.detectLeakedClosableObjects()
.detectLeakedRegistrationObjects()
.penaltyLog()
.penaltyDeath()
.build()
)
}
}
}
Az automatikus ellenőrzéshez a CI/CD-ben használj penaltyDeath-et — ha egy teszt megsérti a szabályzatot, az alkalmazás kivétellel összeomlik. Kombináld az Android Test Orchestratorral, hogy minden teszt tiszta folyamatban fusson. Az UI-tesztekhez (Espresso, Compose Test) írj egyéni TestRule-t, amely elkapja a StrictMode-sértéseket, és assertion-hibává alakítja. Példa: @Before-ben kapcsold be a StrictMode-ot, @After-ban pedig ellenőrizd, hogy nem volt violations.
Alapértelmezés szerint a customSlowCall küszöbe 2000 ms, a disk_read és disk_write esetén pedig nincs küszöb (minden művelet triggerel). A setSlowCallDurationThreshold és a setSlowIoDurationThreshold segítségével saját értékeket adhatsz meg ezredmásodpercben. Ha az alkalmazásod jogszerűen olvas SharedPreferences-t a fő szálon (kis konfiguráció), emeld a küszöböt 10–20 ms-ra — ez kiszűri a gyors olvasásokat, de megtartja a lassakat.
StrictMode hatékony, de szeszélyes eszköz. A rossz beállítás milliónyi hamis riasztást eredményez, amitől a fejlesztők már nem is figyelnek rájuk. Az alábbiakban nagy Android-csapatok tapasztalatából összegyűjtött, kipróbált gyakorlatok következnek.
Ez egy vasszabály: a StrictMode SOHA ne legyen aktív release-verziókban. Használd a BuildConfig.DEBUG jelölőt vagy egy egyéni buildConfigField-et. A release-verziókban sok harmadik féltől származó könyvtár jogszerűen végez műveleteket a fő szálon (SDK-inicializáció, gyorsítótár-írás), és a StrictMode hamis riasztásokat generál. Ráadásul a penaltyDialog release-verzióban párbeszédablakot mutat a végfelhasználónak — ami elfogadhatatlan.
Kis projekteknél (1–10 képernyő) állítsd be a penaltyLog-ot — a naplók elegendőek a kézi elemzéshez. Közepes projekteknél (10–50 képernyő) adj hozzá penaltyDeath-et a network és a customSlowCalls esetén. Nagy projekteknél (50+ képernyő) kapcsold be a teljes szabályzatkészletet penaltyDeath-dzsel a CI/CD-ben, a helyi fejlesztéshez pedig penaltyLog-ot. Ez a fokozatosság nem terheli túl a fejlesztőt hamis crash-ekkel, de szigorúan ellenőrzi a minőséget a pipeline-ban.
Néhány könyvtár (Firebase, Crashlytics, Adjust) jogszerűen végez háttérműveleteket, amelyeket a StrictMode tévesen észlelhet. Megoldások: add a könyvtárat a whitelist-hez a penaltyListener segítségével, frissítsd a könyvtárat olyan verzióra, amely kifejezetten háttérszálra vált, vagy használd a StrictMode.vmPolicy-t. Android 11+-ban megjelent a StrictMode.OnVmViolationListener a sértések programozott szűrésére stacktrace alapján.
// A false positive-ok szűrése a penaltyListener segítségével
StrictMode.setThreadPolicy(
StrictMode.ThreadPolicy.Builder()
.detectAll()
.penaltyListener { violation ->
val stack = violation.stackTraceToString()
if ("com.google.firebase" !in stack) {
logViolation(violation)
}
}
.build()
)
A StrictMode nem az egyetlen minőségellenőrző eszköz az Android-ökoszisztémában. Hogy megértsük a helyét, hasonlítsuk össze az Android Lint, az Android Profiler és a Perfetto eszközökkel a kulcskritériumok szerint: az ellenőrzés ideje, az elemzés mélysége és az automatizálás.
| Krítérium | StrictMode | Android Lint | Profiler / Perfetto |
|---|---|---|---|
| Az ellenőrzés ideje | Runtime (az alkalmazás futása közben) | Compile time (a futtatás előtt) | Runtime (post-mortem) |
| Mit ellenőriz | Lemez, hálózat, szivárgások | XML, kód, erőforrások | CPU, memória, hálózat, energia |
| Automatizálás | CI/CD a penaltyDeath-en keresztül | Gradle-feladat + lint-baseline | Kézi elemzést igényel |
| Mélység | Csak UI-szál és szivárgások | Statikus kódelemzés | Teljes teljesítménykép |
| Hamis riasztások | Közepes (könyvtáraktól függ) | Alacsony (beállított szabályok) | Nincs (tényleges mérések) |
A legjobb stratégia mindhárom megközelítés kombinálása: az Android Lint a fordítási fázisban elkapja a nyilvánvaló hibákat (például az elfelejtett IdleHandler-t), a StrictMode futásidőben észleli a problémákat, az Android Profiler / Perfetto pedig mélyreható elemzésre használható, amikor az első két eszköz nem ad választ. Valós projektekben (Google Maps, Instagram) a StrictMode-ot a fejlesztés második hetében vezetik be — közvetlenül az alaparchitektúra beállítása után.
Nézzünk két valós forgatókönyvet, ahol a StrictMode segít feltárni és megszüntetni a teljesítményproblémákat: a SharedPreferences olvasása a fő szálon, és Activity-szivárgás egy le nem regisztrált callback miatt.
Az alkalmazás indításakor a StrictMode a detectDiskReads szabályzattal észleli a SharedPreferences olvasását a fő szálon. Megoldás: töltsd be a konfigurációt aszinkron módon a CoroutineScope-on keresztül, vagy gyorsítótárazd memóriában az indításkor. A SharedPreferences szinkron módon olvassa a lemezről az XML-fájlt — kis fájl (1–2 KB) esetén is 1–5 ms-ig tart a művelet, olcsó eszközökön pedig akár 20 ms-ig is, ami kimaradt képkockát okozhat.
// ❌ Problémás kód — SharedPrefs olvasása az UI-szálon
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// StrictMode detectDiskReads → VIOLATION!
prefs = getSharedPreferences("config", MODE_PRIVATE)
}
}
// ✅ Javított kód — olvasás Coroutine-on keresztül
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
loadConfigAsync()
}
}
A StrictMode a VmPolicy.detectActivityLeaks segítségével észleli az Activity-t, amely kikerült a stackből (a finish meghívva), de az Activity-objektum statikus hivatkozás vagy le nem regisztrált callback miatt továbbra is a memóriában marad. Tipikus forgatókönyv: EventBus vagy LocationListener regisztrálása a onResume-ben a onPause-ban történő unregister hívása nélkül. A VmPolicy stacktrace-t ad ki, megjelölve azt a sort, ahol a hivatkozás létrejött.
// ❌ Szivárgás — a callback nincs leregisztrálva
private var locationCallback: LocationCallback? = null
override fun onResume() {
super.onResume()
locationCallback = LocationCallback(this::onLocationUpdated)
locationManager.register(locationCallback) // StrictMode → LEAK!
}
override fun onPause() {
super.onPause()
// Elfelejtetted: locationManager.unregister(locationCallback)
}
Gyakran ismételt kérdések
A StrictMode valóban hozzáad egy kis overheadet — minden rendszerhívást ellenőriz a szabályzatoknak való megfelelésre. A teljesítményre gyakorolt hatás 1–3% debug-verzióban, release-ben pedig nincs (ott a StrictMode ki van kapcsolva). A detectAll bekapcsolásakor régebbi eszközökön (Android 6–8) az overhead elérheti az 5%-ot, ezért javasolt csak a szükséges szabályzatokat beállítani.
Igen, a StrictMode teljes mértékben kompatibilis a Jetpack Compose-zal. A disk és network szabályzatok a keretrendszer szintjén működnek, függetlenül az UI-keretrendszertől. Sőt, a Compose-ban az UI-blokkolások kritikussága nagyobb — a Compose 120 FPS-sel rajzolja a képkockákat a magas frissítési frekvenciájú eszközökön, ezért a fájlolvasásra fordított plusz 5 ms észrevehetőbb.
Android 8.1 (API 27) óta a SharedPreferences használhat memóriagyorsítótárat — ha a fájlt már beolvasták, az ismételt olvasás nem váltja ki a StrictMode-ot. Ellenőrizd, hogy először hívod-e meg a getSharedPreferences-t (hideg olvasás), és hogy a detectDiskReads szabályzat aktív-e. Ellenőrizd azt is, hogy a StrictMode-ot nem írták-e felül parent-free fragmentben.
JUnit-tesztekben használd a StrictMode.allowThreadDiskReads() és StrictMode.allowThreadDiskWrites() metódusokat a @Before-ban, a @After-ban pedig a StrictMode.enableDefaults() segítségével állítsd vissza a beállításokat. Instrumentation-tesztekhez használj egyéni TestRunner-t az eredeti szabályzat ideiglenes mentésével. Espresso-tesztekben kényelmes a StrictMode-kitett kódot IdlingResource-be csomagolni.
A StrictMode csak az Android-platformon működik az Android SDK-n keresztül. A Kotlin Multiplatformban (KMP) a commonMain kód nem használhat StrictMode-ot, de az androidMain-hez hozzáadhatod a szokásos módon. Az iOS-részhez használj megfelelőt — a DispatchQueue.main.async assertion-t a fő szálhoz.
Összegzés
Kulcsrakész mobilalkalmazást fejlesztünk
Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.
Olvassa el is