StrictMode: що це, режим строгих правил та дебаг в Android

Автор: IT Sectr Опубліковано: 2026-03-31 Час читання: 8 хв

StrictMode — це вбудований в Android SDK інструмент розробника, який у реальному часі виявляє та повідомляє про випадкові операції введення-виведення та мережеві виклики на головному потоці застосунку. Він не виправляє помилки, а виступає в ролі детектора — викидає винятки або пише в 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 мс призводить до пропуску кадру.

Філософія інструменту

StrictMode дотримується принципу «fail fast» — виявити проблему якомога раніше, бажано в момент її першої появи. Замість того щоб чекати скарг користувачів на гальмування, розробник отримує сигнал (логи, діалог або crash) прямо на етапі розробки. Інструмент не вимагає додаткових бібліотек та конфігурації Gradle — достатньо кількох рядків коду в Application.onCreate, і він працює автоматично на всіх пристроях.

Цільова аудиторія

StrictMode призначений для всіх розробників Android, незалежно від досвіду. Початківцям він допомагає сформувати правильні звички (не робити мережеві запити в UI-потоці), досвідченим — автоматизувати контроль якості в CI/CD пайплайні. Великі проекти (Google, Uber, Spotify) включають StrictMode в debug-збірки з penaltyDeath, а в релізних збірках відключають через перевірку BuildConfig.DEBUG.

Як працює StrictMode

StrictMode перехоплює системні виклики, які можуть блокувати потік, і порівнює їх з набором активних політик. Якщо виклик відповідає політиці та виконується на головному потоці, StrictMode застосовує заданий penalty (покарання). Механізм перехоплення реалізований через внутрішньопроцесний хук — він не використовує рефлексію та працює з мінімальним оверхедом.

Механізм детекції

При активації політики StrictMode впроваджує свій обробник в точку входу системних викликів (FileInputStream, FileOutputStream, Socket, URLConnection). Коли застосунок викликає, наприклад, URLConnection.openStream на головному потоці, StrictMode перевіряє поточний потік — якщо це main thread, інструмент спрацьовує. В Android 6.0+ механізм посилений: мережеві виклики в main thread генерують NetworkOnMainThreadException навіть без StrictMode, але StrictMode дозволяє контролювати і disk I/O.

Penalty (покарання)

Кожна політика може мати свій тип penalty або комбінацію: penaltyLog — запис в LogCat зі стектрейсом, penaltyDialog — показ діалогу користувачеві (тільки в debug), penaltyDeath — викид винятку та краш застосунку, penaltyDropBox — збереження даних в DropBoxManager для подальшого аналізу. Для CI/CD пайплайну рекомендується penaltyDeath — це гарантує, що жоден merge з порушенням не пройде непоміченим.

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 для виявлення небуферизованого введення-виведення.

VmPolicy: витоки пам'яті

VmPolicy контролює витоки на рівні віртуальної машини ART: detectActivityLeaks (Activity, які не були знищені), detectLeakedClosableObjects (незакриті Cursor, Stream, Socket), detectLeakedRegistrationObjects (нескасовані BroadcastReceiver, ServiceConnection). Якщо VmPolicy виявляє, що Activity була створена, але не знищена після виклику onDestroy, він виводить full stack trace — це економить години налагодження витоків пам'яті.

ПолітикаРівеньЩо детектить
detectDiskReadsThreadЧитання SharedPrefs, SQLite, файлів в UI-потоці
detectDiskWritesThreadЗапис в SharedPrefs, SQLite, файли в UI-потоці
detectNetworkThreadБудь-які мережеві операції в UI-потоці
detectActivityLeaksVMActivity, що пережили onDestroy
detectLeakedClosableObjectsVMНезакриті Cursor, Stream, Socket

Користувацькі мітки (customSlowCall)

Через detectCustomSlowCalls можна позначити власні методи як «підозрілі» та отримувати попередження при перевищенні заданого порогу. Наприклад, якщо ваш метод loadUserProfile() зазвичай виконується 5 мс, але в деяких випадках займає 200 мс — оберніть його в StrictMode.noteSlowCall(«loadUserProfile»). Якщо тривалість перевищує поріг (за умовчанням 2000 мс), StrictMode згенерує penalty. Поріг налаштовується через setSlowCallDurationThreshold.

Як налаштувати StrictMode

Базове налаштування StrictMode займає 10 рядків коду та виконується в методі onCreate кастомного Application-класу. Головне правило: StrictMode включається тільки в debug-збірках — в релізних збірках він сповільнює застосунок та може створити помилкові спрацьовування.

Базова конфігурація

Створіть клас, що успадковує 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 мс, для disk_read та disk_write — без порогу (будь-яка операція тригерить). Через setSlowCallDurationThreshold та setSlowIoDurationThreshold можна задати свої значення в мілісекундах. Якщо ваш застосунок легітимно читає SharedPreferences на головному потоці (невеликий конфіг), збільште поріг до 10–20 мс — це відсіче швидкі читання, але залишить повільні.

Кращі практики StrictMode

StrictMode — потужний, але примхливий інструмент. Неправильне налаштування призводить до мільйону помилкових спрацьовувань, через що розробники перестають звертати на них увагу. Нижче — перевірені практики, зібрані з досвіду великих Android-команд.

Вмикайте тільки в debug-збірках

Це залізне правило: StrictMode НІКОЛИ не повинен бути активним в release-збірках. Використовуйте прапорець BuildConfig.DEBUG або кастомний buildConfigField. В release-збірках багато сторонніх бібліотек легітимно виконують операції на головному потоці (ініціалізація SDK, запис кешу), і StrictMode створить помилкові спрацьовування. Більше того, penaltyDialog в release-збірці покаже діалог кінцевому користувачеві — що неприпустимо.

Використовуйте три рівні строгості

Для невеликих проектів (1–10 екранів) налаштуйте penaltyLog — достатньо логів для ручного аналізу. Для середніх проектів (10–50 екранів) додайте penaltyDeath на network та customSlowCalls. Для великих проектів (50+ екранів) увімкніть повний набір політик з penaltyDeath в CI/CD, а для локальної розробки — penaltyLog. Така градація дозволяє не перевантажувати розробника помилковими крашами, але жорстко контролювати якість в пайплайні.

Обробляйте false positives

Деякі бібліотеки (Firebase, Crashlytics, Adjust) легітимно виконують операції в фоні, які StrictMode може помилково детектувати. Рішення: додайте бібліотеку до whitelist через penaltyListener, оновіть бібліотеку до версії з явним перемиканням на background thread, або використовуйте StrictMode.vmPolicy. В Android 11+ з'явився StrictMode.OnVmViolationListener для програмної фільтрації порушень за stacktrace.

kotlin
// Фільтрація false positives через penaltyListener
StrictMode.setThreadPolicy(
    StrictMode.ThreadPolicy.Builder()
        .detectAll()
        .penaltyListener { violation ->
            val stack = violation.stackTraceToString()
            if ("com.google.firebase" !in stack) {
                logViolation(violation)
            }
        }
        .build()
)

StrictMode vs Android Lint vs профайлери

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 або кешувати в memory на старті. SharedPreferences синхронно читає XML-файл з диска — навіть при маленькому файлі (1–2 КБ) операція займає 1–5 мс, а на дешевих пристроях до 20 мс, що може призвести до пропуску кадру.

kotlin
// ❌ Проблемний код — читання SharedPrefs в UI-потоці
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // StrictMode detectDiskReads → ПОРУШЕННЯ!
        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 → ВИТІК!
}

override fun onPause() {
    super.onPause()
    // Забули: locationManager.unregister(locationCallback)
}

Часті запитання

StrictMode уповільнює застосунок?

StrictMode дійсно додає невеликий оверхед — кожен системний виклик перевіряється на відповідність політикам. Вплив на продуктивність становить 1–3% в debug-збірці та відсутній в release (де StrictMode відключається). При включенні detectAll на старих пристроях (Android 6–8) оверхед може досягати 5%, тому рекомендується налаштовувати тільки потрібні політики.

Чи можна використовувати StrictMode з Jetpack Compose?

Так, StrictMode повністю сумісний з Jetpack Compose. Політики disk та network працюють на рівні фреймворку, незалежно від UI-фреймворку. Більше того, в Compose критичність UI-блокувань вища — Compose перемальовує кадри з частотою 120 FPS на пристроях з високою частотою оновлення, тому зайві 5 мс на читання файлу стають помітнішими.

Чому 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
  • Правильна фільтрація false positives — ключ до ефективного використання інструменту
  • Рекомендується впроваджувати StrictMode на другому тижні розробки проекту

Ми розробимо мобільний застосунок під ключ

IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.

Обговорити проект

Читайте також