StrictMode: шта је, начин строгих правила и дебаг у Android-у

Аутор: IT Sectr Објављено: 2026-03-31 Време читања: 8 мин

StrictMode је уграђен у Android SDK алат за програмере који у реалном времену открива и пријављује случајне I/O операције и мрежне позиве на главном ниту апликације. Он не исправља грешке, већ делује као детектор — баца изузетке или пише у LogCat при кршењу задатих политика. Према Google, 2024, правилно подешавање StrictMode омогућава откривање до 80% проблема перформанси пре издавања апликације.

Главно

  • StrictMode — детектор кршења перформанси на главном ниту Android-а
  • Политике диска (disk_read, disk_write) и мреже (network) чине основни скуп провера
  • Алат не поправља проблеме, већ обавештава о њима кроз LogCat, дијалог или crash
  • Подешавање се врши у Application.onCreate и користи setThreadPolicy + setVmPolicy
  • Penalty начини: бацање изузетке (death), логовање, обавештавање у dropbox

Шта је StrictMode

StrictMode је API који је део Android SDK-а од API Level 9 (Android 2.3 Gingerbread). Ъегов задатак је да у реалном времену открије случајно извршавање тешких операција на главном (UI) ниту које могу блокирати рендеровање интерфејса. Главни нит је одговоран за обраду корисничког уноса, рачунање изгледа и рендеровање — свако блокирање дуже од 16 ms доводи до прескацања оквира.

Филозофија алата

StrictMode следи принцип „fail fast“ — откриј проблем што раније могуће, поћељно у тренутку његовог првог појављивања. Уместо да чека жалбе корисника на успорена, програмер добија сигнал (логове, дијалог или crash) већ у фази развоја. Алат не захтева додатне библиотеке или Gradle конфигурацију — довољно је неколико линија кода у Application.onCreate и алат ради аутоматски на свим уређајима.

Циљна публика

StrictMode је намењен свим Android програмерима, без обзира на искуство. Почетницима помаже да формирају правилне навике (да не шаљу мрежне захтеве у UI ниту), искуснима — да аутоматизују контролу квалитета у CI/CD пајплајну. Велики пројекти (Google, Uber, Spotify) укључују StrictMode у debug builds са penaltyDeath, а у release builds га искључују провером BuildConfig.DEBUG.

Како ради StrictMode

StrictMode пресреће системске позиве које могу блокирати нит и упоређује их са скупом активних политика. Ако позив одговара политици и извршава се на главном ниту, StrictMode примењује задати penalty. Механизам пресретања је имплементиран кроз унутарпроцесни хук — не користи reflection и ради са минималним оптерећењем.

Механизам детекције

При активирању политике, StrictMode поставља свој хендлер у улазну тачку системских позива (FileInputStream, FileOutputStream, Socket, URLConnection). Када апликација позове, на пример, URLConnection.openStream на главном ниту, StrictMode проверава тренутни нит — ако је main thread, алат се активира. У Android 6.0+ механизам је појачан: мрежни позиви у main thread-у генеришу NetworkOnMainThreadException чак и без StrictMode-а, али StrictMode омогућава контролу и диск I/O-а.

Penalty (казна)

Свака политика може имати сопствени тип penalty или комбинацију: penaltyLog — упис у LogCat са stacktrace, penaltyDialog — приказ дијалога кориснику (само у debug), penaltyDeath — бацање изузетке и crash апликације, penaltyDropBox — чување података у DropBoxManager-у за каснију анализу. За CI/CD пајплајн се препоручује penaltyDeath — гарантује да ће мергеви са кршењем неће проћи непримећени.

kotlin
class App : Application() {
    override fun onCreate() {
        super.onCreate()
        if (BuildConfig.DEBUG) {
            StrictMode.setThreadPolicy(
                StrictMode.ThreadPolicy.Builder()
                    .detectDiskReads()
                    .detectDiskWrites()
                    .detectNetwork()
                    .penaltyLog()
                    .penaltyDeath()
                    .build()
            )
        }
    }
}

StrictMode политике

StrictMode дели политике на два нивоа: ThreadPolicy (нитске — шта не радити на главном ниту) и VmPolicy (виртуелне машине — цурења меморије и ресурса). Оба нивоа се подешавају независно и раде паралелно.

ThreadPolicy: диск и мрежа

На нивоу нита, StrictMode контролише четири типа кршења: читање са диска (detectDiskReads), писање на диск (detectDiskWrites), мрежне операције (detectNetwork) и прилагођене споре позиве (detectCustomSlowCalls). disk_read се активира при сваком читању SharedPreferences, SQLite, датотека на главном ниту. network — при HTTP захтевима, WebSocket, Socket везама. У Android 11+ додат је detectUnbufferedIO за откривање небуферисаног I/O-а.

VmPolicy: цурења меморије

VmPolicy контролише цурења на нивоу ART виртуелне машине: detectActivityLeaks (Активности које нису уништене), detectLeakedClosableObjects (незатворени Cursor, Stream, Socket), detectLeakedRegistrationObjects (отказани BroadcastReceiver, ServiceConnection). Ако VmPolicy открије да је Активност креирана али није уништена након onDestroy, исписује пун стацк трејс — штеди сате дебагирања цурења меморије.

ПолитикаНивоШта открива
detectDiskReadsThreadЧитање SharedPrefs, SQLite, датотека у UI ниту
detectDiskWritesThreadПисање у SharedPrefs, SQLite, датотеке у UI ниту
detectNetworkThreadСве мрежне операције у UI ниту
detectActivityLeaksVMАктивности које су преживеле onDestroy
detectLeakedClosableObjectsVMНезатворени Cursor, Stream, Socket

Прилагођене ознаке (customSlowCall)

Кроз detectCustomSlowCalls можете означити сопствене методе као „сумњиве“ и добијати упозорење при прекорачу задатог прага. На пример, ако се ваш метод loadUserProfile() обично извршава за 5 ms, али у неким случајевима траје 200 ms — овојте га у StrictMode.noteSlowCall("loadUserProfile"). Ако трајање премаши праг (подразумевано 2000 ms), StrictMode ће генерисати penalty. Праг се подешава кроз setSlowCallDurationThreshold.

Како подесити StrictMode

Основно подешавање StrictMode заузима 10 линија кода и врши се у onCreate методи прилагођене Application класе. Главно правило: StrictMode се укључује само у debug builds — у release builds успорава апликацију и може да створи лажне аларме.

Основно подешавање

Креирајте класу која наслеђује Application, региструјте је у AndroidManifest.xml кроз атрибут android:name и додајте конфигурацију StrictMode. ThreadPolicy.Builder укључује све детекторе и све врсте penalty (осим dialog — ради само са повезаним дебагером). VmPolicy.Builder додаје детекторе цурења Activity и Closable. За велике пројекте (100+ екрана) препоручује се подешавање VmPolicy са penaltyDeath на детектор Activity Leaks — строго је, али ефикасно.

kotlin
class App : Application() {
    override fun onCreate() {
        super.onCreate()
        if (BuildConfig.DEBUG) {
            StrictMode.setVmPolicy(
                StrictMode.VmPolicy.Builder()
                    .detectActivityLeaks()
                    .detectLeakedClosableObjects()
                    .detectLeakedRegistrationObjects()
                    .penaltyLog()
                    .penaltyDeath()
                    .build()
            )
        }
    }
}

Интеграција у CI/CD

За аутоматску контролу у CI/CD користите penaltyDeath — ако неки тест прекрши политику, апликација ће пасти са изузетком. Комбинујте са Android Test Orchestrator-ом да сваки тест ради у чистом процесу. За UI тестове (Espresso, Compose Test) напишите прилагођени TestRule који пресреће StrictMode кршења и претвара их у assertion failure. Пример: у @Before укључите StrictMode, а у @After проверите да није било violations.

Подешавање граница

Подразумевано, праг за customSlowCall је 2000 ms, за disk_read и disk_write — без прага (свака операција га покреће). Кроз setSlowCallDurationThreshold и setSlowIoDurationThreshold можете поставити сопствене вредности у милисекундама. Ако ваша апликација легитимно чита SharedPreferences на главном ниту (мала конфигурација), повећајте праг на 10–20 ms — пресећи ће брзе операције, али ће задржати споре.

Најбоље праксе StrictMode

StrictMode је моћан, али хирови алат. Неправилно подешавање доводи до милион лажних аларма, због чега програмери престају да их примећују. Испод су проверене праксе прикупљене из искуства великих Android тимова.

Укључујте само у debug builds

Ово је гвоздено правило: StrictMode НИКАДА не сме да буде активан у release builds. Користите заставицу BuildConfig.DEBUG или прилагођену buildConfigField. У release builds, многе библиотеке трећих страна легитимно извршавају операције на главном ниту (иницијализација SDK, писање кеша), и StrictMode ће створити лажне аларме. Штавише, penaltyDialog у release build-у ће приказати дијалог крањем кориснику — што је неприхватљиво.

Користите три нивоа строгости

За мале пројекте (1–10 екрана) подесите penaltyLog — логови су довољни за речну анализу. За средње пројекте (10–50 екрана) додајте penaltyDeath за network и customSlowCalls. За велике пројекте (50+ екрана) укључите пун скуп политика са penaltyDeath у CI/CD, а за локални развој — penaltyLog. Ова градација омогућава да не оптерећујете програмера лажним crash-евима, али да строго контролишете квалитет у пајплајну.

Обрађите лажне позитиве

Неке библиотеке (Firebase, Crashlytics, Adjust) легитимно извршавају операције у позадини које StrictMode може погрешно да детектује. Решења: додајте библиотеку у whitelist кроз penaltyListener, ажурирајте библиотеку на верзију са јасним преласком на background thread или користите StrictMode.vmPolicy. У Android 11+ појавио се StrictMode.OnVmViolationListener за програмско филтрирање кршења по stacktrace-у.

kotlin
// Филтрирање лажних позитива кроз penaltyListener
StrictMode.setThreadPolicy(
    StrictMode.ThreadPolicy.Builder()
        .detectAll()
        .penaltyListener { violation ->
            val stack = violation.stackTraceToString()
            if ("com.google.firebase" !in stack) {
                logViolation(violation)
            }
        }
        .build()
)

StrictMode наспрам Android Lint-а и профајлера

StrictMode није једини алат за контролу квалитета у Android екосистему. Да бисмо разумели његово место, упоредимо га са Android Lint, Android Profiler и Perfetto по кључним критеријума: време провере, дубина анализе и аутоматизација.

КритеријумStrictModeAndroid LintProfiler / Perfetto
Време провереRuntime (током рада)Compile time (пре покретања)Runtime (post-mortem)
Шта провераваДиск, мрежа, цурењаXML, код, ресурсиCPU, меморија, мрежа, енергија
АутоматизацијаCI/CD кроз penaltyDeathGradle task + lint-baselineЗахтева речну анализу
ДубинаСамо UI нит и цурењаСтатичка анализа кодаПуна слика перформанси
Лажни алармиСредњи (зависе од библиотека)Ниски (подешена правила)Нема (стварна мерења)

Најбоља стратегија је комбинација сва три приступа: Android Lint хвата очигледне грешке у фази компилације (на пример, заборављени IdleHandler), StrictMode открива проблеме у радном окружењу, а Android Profiler / Perfetto се користи за дубинску анализу када прва два алата не дају одговор. У реалним пројектима (Google Maps, Instagram) StrictMode се примењује у другој недељи развоја — одмах након подешавања основне архитектуре.

Примери кода са StrictMode

Размотримо два реална сценарија у којима StrictMode помаже да се открију и отклоне проблеми перформанси: читање SharedPreferences на главном ниту и цурење Activity кроз нерегистровани callback.

Детекција спорог SharedPreferences

При покретању апликације, StrictMode са политиком detectDiskReads ће открити читање SharedPreferences на главном ниту. Решење: учитајте конфигурацију асинхроно кроз CoroutineScope или кеширајте у меморији при покретању. SharedPreferences синхроно чита XML датотеку са диска — чак и код мале датотеке (1–2 KB), операција траје 1–5 ms, а на јефтиним уређајима до 20 ms, што може довести до прескацања оквира.

kotlin
// ❌ Проблематичан код — читање 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()
    }
}

Откривање цурења Activity

StrictMode са VmPolicy.detectActivityLeaks ће открити Activity која је изашла из стека (finish је позван), али објекат Activity и даље виси у меморији због статичке референце или нерегистрованог callback-а. Типичан сценариј: регистрација EventBus или LocationListener у onResume без позива unregister у onPause. VmPolicy ће исписати стацк трејс са назнаком линије где је креирана референца.

kotlin
// ❌ Цурење — 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 успорава апликацију?

StrictMode заиста додаје мали оптерећење — сваки системски позив се проверава на усклађеност са политикама. Утицај на перформансу је 1–3% у debug build-у и не постоји у release-у (где је StrictMode искључен). При укључивању detectAll на старијим уређајима (Android 6–8) оптерећење може досећи 5%, па се препоручује подешавање само потребних политика.

Могу ли да користим StrictMode са Jetpack Compose?

Да, StrictMode је потпуно компатибилан са Jetpack Compose. Политике диска и мреже раде на нивоу оквира, независно од UI оквира. Штавише, у Compose-у критичност UI блокада је већа — Compose прецртава оквире на 120 FPS на уређајима са високом фреквенцијом освежавања, па додатних 5 ms за читање датотеке постаје приметнији.

Зашто StrictMode не ради код читања SharedPreferences?

Од Android 8.1 (API 27), SharedPreferences може да користи кеширање у меморији — ако је датотека већ прочитана, поновно читање не покреће StrictMode. Проверите да ли први пут позивате getSharedPreferences (хладно читање) и да ли је политика detectDiskReads активна. Такође проверите да ли StrictMode није прегазан у parent-free фрагменту.

Како искључити StrictMode за појединачне тестове?

У JUnit тестовима користите StrictMode.allowThreadDiskReads() и StrictMode.allowThreadDiskWrites() у @Before, а у @After вратите поставке кроз StrictMode.enableDefaults(). За Instrumentation тестове користите прилагођени TestRunner са привременим чувањем оригиналне политике. У Espresso тестовима згодно је завити код осетљив на StrictMode у IdlingResource.

Да ли је StrictMode потребан у Kotlin Multiplatform?

StrictMode ради само на Android платформи кроз Android SDK. У Kotlin Multiplatform (KMP) код commonMain не може да користи StrictMode, али за androidMain га можете додати као обично. За iOS део користите аналог — DispatchQueue.main.async assertion за главни нит.

Закључак

  • StrictMode — детектор у реалном времену проблема перформанси на главном ниту Android-а
  • Политике се деле на ThreadPolicy (диск, мрежа) и VmPolicy (цурења меморије)
  • Подешавање заузима 10 линија кода у Application.onCreate са провером BuildConfig.DEBUG
  • За CI/CD користите penaltyDeath — кршење политике доводи до пада апликације
  • StrictMode не замењује, већ употпуњује Android Lint и Perfetto
  • Исправно филтрирање лажних позитива је кључ за ефикасно кориштење алата
  • Препоручује се примена StrictMode-а у другој недељи развоја пројекта

Развићемо мобилну апликацију под кључ

IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.

Разговарајте о пројекту

Прочитајте такође