StrictMode: What It Is, Strict Mode Rules and Debug in Android

Author: IT Sectr Published: 2026-03-31 Reading time: 8 min

StrictMode is a developer tool built into the Android SDK that detects and reports accidental I/O operations and network calls on the main thread of the application in real time. It doesn't fix errors but acts as a detector — throwing exceptions or writing to LogCat when configured policies are violated. According to Google, 2024, proper StrictMode configuration can detect up to 80% of performance issues before the app is released.

Key Takeaways

  • StrictMode — a detector of performance violations on the Android main thread
  • Disk policies (disk_read, disk_write) and network (network) form the basic set of checks
  • The tool doesn't fix problems but notifies about them via LogCat, dialog, or crash
  • Configuration is done in Application.onCreate using setThreadPolicy + setVmPolicy
  • Penalty modes: exception throw (death), logging, dropbox notification

What is StrictMode

StrictMode is an API included in the Android SDK since API Level 9 (Android 2.3 Gingerbread). Its task is to detect at runtime the accidental execution of heavy operations on the main (UI) thread that could block UI rendering. The main thread handles user input processing, layout calculation, and rendering — any block longer than 16 ms results in a frame drop.

Tool Philosophy

StrictMode follows the “fail fast” principle — detect the problem as early as possible, ideally at the moment of its first appearance. Instead of waiting for user complaints about slowdowns, the developer receives a signal (logs, dialog, or crash) right at the development stage. The tool doesn't require additional libraries or Gradle configuration — just a few lines of code in Application.onCreate, and it works automatically on all devices.

Target Audience

StrictMode is designed for all Android developers, regardless of experience. For beginners, it helps form good habits (don't make network requests on the UI thread); for experienced developers, it automates quality control in the CI/CD pipeline. Large projects (Google, Uber, Spotify) include StrictMode in debug builds with penaltyDeath, and disable it in release builds via the BuildConfig.DEBUG check.

How StrictMode Works

StrictMode intercepts system calls that could block the thread and compares them against the set of active policies. If a call matches a policy and is executed on the main thread, StrictMode applies the specified penalty. The interception mechanism is implemented via an in-process hook — it doesn't use reflection and operates with minimal overhead.

Detection Mechanism

When the StrictMode policy is activated, it injects its handler into the entry points of system calls (FileInputStream, FileOutputStream, Socket, URLConnection). When the application calls, for example, URLConnection.openStream on the main thread, StrictMode checks the current thread — if it's the main thread, the tool triggers. In Android 6.0+, the mechanism is enhanced: network calls on the main thread generate NetworkOnMainThreadException even without StrictMode, but StrictMode also allows controlling disk I/O.

Penalty

Each policy can have its own penalty type or combination: penaltyLog — writes to LogCat with a stack trace, penaltyDialog — shows a dialog to the user (debug only), penaltyDeath — throws an exception and crashes the app, penaltyDropBox — saves data to DropBoxManager for later analysis. For CI/CD pipelines, penaltyDeath is recommended — it ensures that no merge with violations goes unnoticed.

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

StrictMode Policies

StrictMode divides policies into two levels: ThreadPolicy (thread-level — what cannot be done on the main thread) and VmPolicy (virtual machine — memory and resource leaks). Both levels are configured independently and work in parallel.

ThreadPolicy: Disk and Network

At the thread level, StrictMode controls four types of violations: disk reads (detectDiskReads), disk writes (detectDiskWrites), network operations (detectNetwork), and custom slow calls (detectCustomSlowCalls). disk_read triggers on any reading of SharedPreferences, SQLite, or files on the main thread. network triggers on HTTP requests, WebSocket, and Socket connections. In Android 11+, detectUnbufferedIO was added for detecting unbuffered I/O.

VmPolicy: Memory Leaks

VmPolicy controls leaks at the ART virtual machine level: detectActivityLeaks (activities that were not destroyed), detectLeakedClosableObjects (unclosed Cursor, Stream, Socket), detectLeakedRegistrationObjects (unregistered BroadcastReceiver, ServiceConnection). If VmPolicy detects that an Activity was created but not destroyed after onDestroy was called, it outputs a full stack trace — saving hours of memory leak debugging.

PolicyLevelWhat It Detects
detectDiskReadsThreadReading SharedPrefs, SQLite, files on the UI thread
detectDiskWritesThreadWriting to SharedPrefs, SQLite, files on the UI thread
detectNetworkThreadAny network operations on the UI thread
detectActivityLeaksVMActivities surviving onDestroy
detectLeakedClosableObjectsVMUnclosed Cursor, Stream, Socket

Custom Slow Calls

Through detectCustomSlowCalls, you can mark your own methods as “suspicious” and receive a warning when a specified threshold is exceeded. For example, if your loadUserProfile() method usually takes 5 ms but sometimes takes 200 ms — wrap it in StrictMode.noteSlowCall(“loadUserProfile”). If the duration exceeds the threshold (default 2000 ms), StrictMode will generate a penalty. The threshold is configured via setSlowCallDurationThreshold.

How to Configure StrictMode

Basic StrictMode configuration takes 10 lines of code and is done in the onCreate method of a custom Application class. The main rule: StrictMode is enabled only in debug builds — in release builds it slows down the app and can create false positives.

Basic Configuration

Create a class extending Application, register it in AndroidManifest.xml via the android:name attribute, and add StrictMode configuration. ThreadPolicy.Builder includes all detectors and all penalty types (except dialog — it works only when a debugger is attached). VmPolicy.Builder adds detectors for Activity leaks and Closable objects. For large projects (100+ screens), it's recommended to configure VmPolicy with penaltyDeath on the Activity Leaks detector — it's strict but effective.

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 Integration

For automatic control in CI/CD, use penaltyDeath — if any test violates the policy, the app will crash with an exception. Combine with Android Test Orchestrator so each test runs in a clean process. For UI tests (Espresso, Compose Test), write a custom TestRule that intercepts StrictMode violations and turns them into assertion failures. Example: in @Before, enable StrictMode, and in @After, check that there were no violations.

Threshold Configuration

By default, the threshold for customSlowCall is 2000 ms, for disk_read and disk_write — no threshold (any operation triggers). Through setSlowCallDurationThreshold and setSlowIoDurationThreshold, you can set your own values in milliseconds. If your app legitimately reads SharedPreferences on the main thread (small config), increase the threshold to 10–20 ms — this will filter out fast reads while keeping slow ones.

StrictMode Best Practices

StrictMode is a powerful but finicky tool. Incorrect configuration leads to millions of false positives, causing developers to stop paying attention to them. Below are proven practices gathered from the experience of large Android teams.

Enable Only in Debug Builds

This is a hard rule: StrictMode should NEVER be active in release builds. Use the BuildConfig.DEBUG flag or a custom buildConfigField. In release builds, many third-party libraries legitimately perform operations on the main thread (SDK initialization, cache writing), and StrictMode will create false positives. Moreover, penaltyDialog in a release build will show a dialog to the end user — which is unacceptable.

Use Three Levels of Strictness

For small projects (1–10 screens), configure penaltyLog — logs are enough for manual analysis. For medium projects (10–50 screens), add penaltyDeath on network and customSlowCalls. For large projects (50+ screens), enable the full set of policies with penaltyDeath in CI/CD, and penaltyLog for local development. This gradation prevents overloading developers with false crashes while strictly controlling quality in the pipeline.

Handling False Positives

Some libraries (Firebase, Crashlytics, Adjust) legitimately perform background operations that StrictMode might incorrectly detect. Solutions: add the library to a whitelist via penaltyListener, update the library to a version with explicit background thread switching, or use StrictMode.vmPolicy. In Android 11+, StrictMode.OnVmViolationListener was introduced for programmatic filtering of violations by stack trace.

kotlin
// Filtering false positives via 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 Profilers

StrictMode is not the only quality control tool in the Android ecosystem. To understand its place, let's compare it with Android Lint, Android Profiler, and Perfetto across key criteria: check time, analysis depth, and automation.

CriterionStrictModeAndroid LintProfiler / Perfetto
Check TimeRuntime (while the app is running)Compile time (before launch)Runtime (post-mortem)
What It ChecksDisk, network, leaksXML, code, resourcesCPU, memory, network, power
AutomationCI/CD via penaltyDeathGradle task + lint-baselineRequires manual analysis
DepthUI thread only and leaksStatic code analysisFull performance picture
False PositivesMedium (depends on libraries)Low (configured rules)None (actual measurements)

The best strategy is to combine all three approaches: Android Lint catches obvious errors at compile time (e.g., a forgotten IdleHandler), StrictMode detects problems at runtime, and Android Profiler / Perfetto is used for deep analysis when the first two tools don't provide answers. In real projects (Google Maps, Instagram), StrictMode is introduced in the second week of development — right after setting up the basic architecture.

StrictMode Code Examples

Let's look at two real scenarios where StrictMode helps detect and fix performance issues: reading SharedPreferences on the main thread and Activity leak through an unregistered callback.

Detecting Slow SharedPreferences

At app startup, StrictMode with the detectDiskReads policy will detect reading SharedPreferences on the main thread. Solution: load configuration asynchronously via CoroutineScope or cache it in memory at startup. SharedPreferences synchronously reads an XML file from disk — even with a small file (1–2 KB), the operation takes 1–5 ms, and up to 20 ms on cheap devices, which can lead to frame drops.

kotlin
// ❌ Problem code — reading SharedPrefs on UI thread
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // StrictMode detectDiskReads → VIOLATION!
        prefs = getSharedPreferences("config", MODE_PRIVATE)
    }
}

// ✅ Fixed code — reading via Coroutine
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        loadConfigAsync()
    }
}

Detecting Activity Leaks

StrictMode with VmPolicy.detectActivityLeaks will detect an Activity that has exited the stack (finish was called) but the Activity object remains in memory due to a static reference or an unregistered callback. Typical scenario: registering EventBus or LocationListener in onResume without calling unregister in onPause. VmPolicy will output a stack trace indicating the line where the reference was created.

kotlin
// ❌ Leak — callback not cancelled
private var locationCallback: LocationCallback? = null

override fun onResume() {
    super.onResume()
    locationCallback = LocationCallback(this::onLocationUpdated)
    locationManager.register(locationCallback) // StrictMode → LEAK!
}

override fun onPause() {
    super.onPause()
    // Forgot: locationManager.unregister(locationCallback)
}

Frequently Asked Questions

Does StrictMode slow down the app?

StrictMode does add a small overhead — every system call is checked against the policies. The performance impact is 1–3% in debug builds and is absent in release (where StrictMode is disabled). When enabling detectAll on older devices (Android 6–8), overhead can reach 5%, so it's recommended to configure only the necessary policies.

Can StrictMode be used with Jetpack Compose?

Yes, StrictMode is fully compatible with Jetpack Compose. The disk and network policies work at the framework level, independent of the UI framework. Moreover, in Compose the criticality of UI blocks is higher — Compose redraws frames at 120 FPS on high-refresh-rate devices, so an extra 5 ms on file reading becomes more noticeable.

Why doesn't StrictMode trigger on SharedPreferences reading?

Starting from Android 8.1 (API 27), SharedPreferences may use in-memory caching — if the file has already been read, re-reading won't trigger StrictMode. Make sure you're calling getSharedPreferences for the first time (cold read) and that the detectDiskReads policy is active. Also check that StrictMode hasn't been overridden in a parent-free fragment.

How to disable StrictMode for individual tests?

In JUnit tests, use StrictMode.allowThreadDiskReads() and StrictMode.allowThreadDiskWrites() in @Before, and restore settings in @After via StrictMode.enableDefaults(). For Instrumentation tests, use a custom TestRunner with temporary preservation of the original policy. In Espresso tests, it's convenient to wrap StrictMode-sensitive code in an IdlingResource.

Is StrictMode needed in Kotlin Multiplatform?

StrictMode only works on the Android platform through the Android SDK. In Kotlin Multiplatform (KMP), commonMain code cannot use StrictMode, but for androidMain you can add it as usual. For the iOS part, use an analog — DispatchQueue.main.async assertion for the main thread.

Summary

  • StrictMode — a runtime detector of performance issues on the Android main thread
  • Policies are divided into ThreadPolicy (disk, network) and VmPolicy (memory leaks)
  • Configuration takes 10 lines of code in Application.onCreate with a BuildConfig.DEBUG check
  • For CI/CD, use penaltyDeath — a policy violation crashes the app
  • StrictMode doesn't replace but complements Android Lint and Perfetto
  • Proper false positive filtering is the key to effective tool usage
  • It's recommended to introduce StrictMode in the second week of project development

We will develop a mobile application turnkey

IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.

Discuss the project

Read also