---
title: "StrictMode no Android com Kotlin: detecte travamentos e I/O na main thread"
url: "https://kotlin.dev.br/blog/strictmode-android-kotlin-performance-2026/"
markdown_url: "https://kotlin.dev.br/blog/strictmode-android-kotlin-performance-2026.MD"
description: "Use StrictMode no Android com Kotlin para detectar disco, rede e recursos não fechados na main thread, configurar penalidades e corrigir travamentos no app."
date: "2026-08-22"
author: "Karina Melo"
---

# StrictMode no Android com Kotlin: detecte travamentos e I/O na main thread

Use StrictMode no Android com Kotlin para detectar disco, rede e recursos não fechados na main thread, configurar penalidades e corrigir travamentos no app.


**Resposta rápida:** ative o **StrictMode** apenas nas builds de desenvolvimento para encontrar operações de disco ou rede na main thread, recursos que não foram fechados e outros usos incorretos da plataforma Android. Configure uma `ThreadPolicy` para violações ligadas à thread e uma `VmPolicy` para problemas do processo, registre os achados com `penaltyLog()` e corrija a causa em vez de liberar tudo com `permitAll()`. StrictMode é uma ferramenta de diagnóstico: ele ajuda a revelar trabalho bloqueante e vazamentos, mas não mede sozinho a experiência do usuário, não garante ausência de ANR e não deve derrubar o app de produção por padrão.

Um app pode parecer rápido no aparelho de quem desenvolve e travar em um celular intermediário quando abre um arquivo, consulta o banco ou inicializa uma biblioteca. Parte desses problemas nasce de operações curtas demais para chamar atenção durante o desenvolvimento, mas longas o suficiente para atrasar frames, cliques e transições.

O **StrictMode** transforma vários desses comportamentos em sinais visíveis no Logcat. Ele funciona como uma cerca de proteção para o ciclo de desenvolvimento: quando uma regra é violada, a plataforma registra, destaca ou pune a operação conforme a política configurada.

Neste guia, você vai aprender a ativar StrictMode com Kotlin, entender a diferença entre `ThreadPolicy` e `VmPolicy`, interpretar violações, corrigir I/O bloqueante com coroutines e incorporar a ferramenta ao fluxo do time. Para medir o resultado depois das correções, combine a leitura com [Baseline Profiles e Macrobenchmark](/blog/baseline-profiles-macrobenchmark-android-kotlin-2026/) e com o guia de [Crashlytics e ANR no Android](/blog/firebase-crashlytics-anr-android-kotlin-2026/).

## O que é StrictMode no Android?

StrictMode é uma API de diagnóstico do Android que detecta determinadas operações potencialmente perigosas durante a execução do app. Ela é especialmente útil para encontrar trabalho bloqueante feito na **main thread**, responsável por processar entrada do usuário, ciclo de vida e renderização da interface.

Se essa thread fica ocupada com leitura de arquivo, consulta lenta, resolução de rede ou inicialização pesada, a interface deixa de responder. Um atraso pequeno pode causar jank. Um bloqueio prolongado pode contribuir para um **Application Not Responding (ANR)**.

StrictMode não impede todas essas situações automaticamente. Você declara quais violações deseja detectar e quais penalidades aplicar. Por exemplo:

- registrar leitura e escrita em disco na main thread;
- detectar acesso de rede na thread de UI;
- avisar sobre objetos fecháveis abandonados;
- encontrar alguns vazamentos relacionados a componentes Android;
- piscar a tela durante certas violações em builds de desenvolvimento;
- encerrar o processo para tornar uma regra impossível de ignorar em testes controlados.

A API trabalha com duas famílias de políticas:

| Política | Escopo | Exemplos de problema |
|---|---|---|
| `StrictMode.ThreadPolicy` | operações executadas por uma thread | disco e rede na main thread |
| `StrictMode.VmPolicy` | comportamento geral da VM e do processo | recursos não fechados e alguns vazamentos de componentes |

A separação é importante. Uma leitura de arquivo no clique de um botão é uma violação de thread. Um `Closeable` abandonado sem `close()` é um problema observado pela política da VM.

## Como ativar StrictMode com Kotlin

A configuração mais segura fica na classe `Application` e é condicionada à build de debug. Dessa forma, todos os fluxos de desenvolvimento recebem a política sem introduzir penalidades deliberadas no artefato de produção.

```kotlin
class KotlinBrasilApplication : Application() {
    override fun onCreate() {
        super.onCreate()

        if (BuildConfig.DEBUG) {
            enableStrictMode()
        }
    }

    private fun enableStrictMode() {
        StrictMode.setThreadPolicy(
            StrictMode.ThreadPolicy.Builder()
                .detectAll()
                .penaltyLog()
                .build()
        )

        StrictMode.setVmPolicy(
            StrictMode.VmPolicy.Builder()
                .detectAll()
                .penaltyLog()
                .build()
        )
    }
}
```

Registre a `Application` no manifesto, caso o projeto ainda não tenha uma:

```xml
<application
    android:name=".KotlinBrasilApplication"
    ... />
```

`detectAll()` é um bom começo para uma build local porque habilita as detecções disponíveis para a versão do Android em execução. Entretanto, a lista concreta pode variar conforme o nível da API. Quando o time precisa de comportamento previsível ou está adotando StrictMode gradualmente em um app legado, declarar detectores específicos costuma ser melhor.

```kotlin
private fun enableStrictMode() {
    val threadPolicy = StrictMode.ThreadPolicy.Builder()
        .detectDiskReads()
        .detectDiskWrites()
        .detectNetwork()
        .penaltyLog()
        .build()

    val vmPolicy = StrictMode.VmPolicy.Builder()
        .detectLeakedClosableObjects()
        .detectActivityLeaks()
        .penaltyLog()
        .build()

    StrictMode.setThreadPolicy(threadPolicy)
    StrictMode.setVmPolicy(vmPolicy)
}
```

Use apenas detectores disponíveis no `minSdk` e no nível de API em que a configuração é executada. O Android Studio indica chamadas que exigem uma versão mais nova; quando necessário, proteja a regra com uma verificação de `Build.VERSION.SDK_INT`.

## O que as penalidades fazem?

Detecção e penalidade são conceitos diferentes. O detector reconhece o comportamento; a penalidade define como o app reage.

### `penaltyLog()`

Registra a violação no Logcat com stack trace. É a opção mais prática para começar porque não altera drasticamente o fluxo do app.

No Android Studio, filtre por `StrictMode` e procure mensagens como:

```text
StrictMode policy violation: android.os.strictmode.DiskReadViolation
```

O stack trace mostra o caminho que levou à operação. Leia primeiro os frames pertencentes ao seu pacote. O ponto onde a plataforma efetivamente lê o disco pode estar dentro de `SharedPreferences`, SQLite, uma biblioteca ou carregamento de arquivo, mas a decisão arquitetural geralmente está alguns frames acima.

### `penaltyFlashScreen()`

Pode destacar visualmente determinadas violações durante o uso manual. É útil em sessões de QA porque revela que uma interação aparentemente simples disparou trabalho bloqueante.

Não dependa apenas dela: o efeito visual não explica a causa e pode não representar todas as violações.

### `penaltyDeath()`

Encerra o processo quando ocorre uma violação compatível. Essa penalidade torna o erro impossível de ignorar, mas pode deixar o desenvolvimento impraticável quando ativada cedo em um projeto antigo.

Uma estratégia melhor é começar com log, eliminar o backlog e reservar `penaltyDeath()` para testes específicos, builds internas rigorosas ou regras que a equipe já consegue cumprir.

### Listener personalizado

Em versões compatíveis do Android, você pode receber violações por callback e encaminhá-las a um logger interno da build de desenvolvimento:

```kotlin
val executor = Executors.newSingleThreadExecutor()

val policy = StrictMode.ThreadPolicy.Builder()
    .detectAll()
    .penaltyListener(executor) { violation ->
        Log.e("StrictModeReporter", "Violação detectada", violation)
    }
    .build()

StrictMode.setThreadPolicy(policy)
```

Não envie dados sensíveis ou stack traces indiscriminadamente a um serviço externo. Para produção, avalie privacidade, volume e custo antes de criar qualquer telemetria semelhante. Na maioria dos projetos, StrictMode permanece local ou restrito a builds internas.

## Exemplo: leitura de arquivo na main thread

Considere uma tela que carrega um JSON de `assets` durante o `onCreate`:

```kotlin
class CatalogActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val json = assets.open("catalog.json")
            .bufferedReader()
            .use { it.readText() }

        renderCatalog(json)
    }
}
```

A leitura é síncrona e ocorre na main thread. Em um arquivo pequeno e com cache quente, o problema pode passar despercebido. Em armazenamento lento ou durante pressão de recursos, a abertura da tela fica mais cara.

Mova a operação bloqueante para `Dispatchers.IO` e volte à main thread apenas para atualizar a UI:

```kotlin
class CatalogViewModel(
    private val repository: CatalogRepository,
) : ViewModel() {

    private val _state = MutableStateFlow<CatalogState>(CatalogState.Loading)
    val state: StateFlow<CatalogState> = _state.asStateFlow()

    fun load() {
        viewModelScope.launch {
            _state.value = CatalogState.Loading

            _state.value = runCatching {
                repository.loadCatalog()
            }.fold(
                onSuccess = { CatalogState.Success(it) },
                onFailure = { CatalogState.Error(it) },
            )
        }
    }
}

class CatalogRepository(
    private val assetReader: AssetReader,
) {
    suspend fun loadCatalog(): Catalog = withContext(Dispatchers.IO) {
        assetReader.readCatalog()
    }
}
```

A correção não consiste apenas em “colocar uma coroutine”. `viewModelScope.launch` inicia na main thread por padrão. O trabalho bloqueante precisa atravessar explicitamente uma fronteira adequada, como `withContext(Dispatchers.IO)`, ou ser executado por uma API assíncrona.

Também preserve cancelamento e estado de erro. Jogar uma leitura em um `GlobalScope` remove o alerta da main thread, mas cria um fluxo sem dono e pode atualizar uma tela que já morreu. O guia de [structured concurrency em Kotlin](/blog/coroutines-avancadas-structured-concurrency-kotlin/) aprofunda esse ponto.

## SharedPreferences e DataStore: por que leituras pequenas também aparecem?

`SharedPreferences` parece uma simples tabela em memória, mas seu carregamento e persistência podem tocar o disco. Inicializar preferências em um caminho crítico, especialmente durante startup, pode gerar uma violação ou contribuir para tempo até o primeiro frame.

Não transforme toda chamada em uma exceção local. Primeiro pergunte:

1. esse dado precisa ser lido antes da primeira tela?
2. ele pode ser carregado de forma assíncrona?
3. a UI consegue começar com um estado padrão ou de loading?
4. o dado pertence a preferências ou deveria estar em banco/cache estruturado?

Para dados reativos e assíncronos, [DataStore com Kotlin](/tutoriais/datastore-preferences-kotlin/) costuma oferecer um contrato melhor. Ainda assim, coletar dados de forma errada ou executar transformação pesada na main thread continua sendo um problema. Trocar a biblioteca não elimina a necessidade de medir.

## Banco de dados na main thread

Room bloqueia por padrão várias consultas indevidas na main thread justamente porque banco pode envolver disco, locks, migração e materialização de objetos. Não use `allowMainThreadQueries()` como solução para um alerta de StrictMode.

Prefira APIs `suspend`, `Flow` ou paginação:

```kotlin
@Dao
interface ArticleDao {
    @Query("SELECT * FROM article ORDER BY publishedAt DESC")
    fun observeArticles(): Flow<List<ArticleEntity>>

    @Query("SELECT * FROM article WHERE id = :id")
    suspend fun findById(id: Long): ArticleEntity?
}
```

Em Compose, colete o `Flow` respeitando o ciclo de vida. Para listas grandes, o guia de [Paging 3 com Kotlin e Compose](/blog/paging-3-kotlin-compose-2026/) mostra como evitar carregar toda a tabela de uma vez. Se a violação aparece na abertura do banco, verifique também migrações, callbacks e inicialização precoce; o artigo sobre [migrações do Room](/blog/migrations-room-android-kotlin-2026/) cobre os riscos de schema.

## Recursos não fechados com `VmPolicy`

Arquivos, streams, cursores e outros objetos fecháveis podem reter descritores nativos quando não são encerrados. Em Kotlin, a função `use` é a forma idiomática de garantir fechamento:

```kotlin
fun readText(inputStream: InputStream): String =
    inputStream.bufferedReader().use { reader ->
        reader.readText()
    }
```

Mesmo quando ocorre uma exceção, `use` fecha o recurso. Evite guardar um `InputStream`, `Cursor` ou `ParcelFileDescriptor` em singleton ou propriedade de vida longa sem um contrato explícito de propriedade e fechamento.

Uma `VmPolicy` com `detectLeakedClosableObjects()` pode ajudar a encontrar esses casos. O aviso pode aparecer depois da coleta de lixo, não exatamente no momento em que o recurso foi abandonado. Por isso, o stack trace de alocação e o caminho de abertura são valiosos.

## StrictMode com Jetpack Compose

Compose não muda a regra principal: a composição e os callbacks de UI normalmente executam na main thread. Portanto, não faça leitura bloqueante dentro de um composable, `remember`, `LaunchedEffect` ou callback esperando que a API mova o trabalho automaticamente.

Exemplo incorreto:

```kotlin
@Composable
fun ProfileScreen(repository: ProfileRepository) {
    val profile = remember {
        repository.loadProfileBlocking()
    }

    Text(profile.name)
}
```

`remember` memoriza o resultado entre recomposições compatíveis, mas a primeira execução continua bloqueando a thread atual.

Prefira um estado produzido pelo ViewModel:

```kotlin
@Composable
fun ProfileRoute(viewModel: ProfileViewModel) {
    val state by viewModel.state.collectAsStateWithLifecycle()

    when (val current = state) {
        ProfileState.Loading -> CircularProgressIndicator()
        is ProfileState.Content -> Text(current.profile.name)
        is ProfileState.Error -> ErrorMessage(current.message)
    }
}
```

Se uma transformação é CPU-intensiva, `Dispatchers.IO` também não é uma resposta universal. Trabalho de CPU deve ser avaliado para `Dispatchers.Default`, processamento incremental ou mudança de algoritmo. StrictMode detecta algumas categorias de uso indevido; profiler, traces e benchmarks ajudam a encontrar o restante.

## Quando usar `permitDiskReads()` ou outras liberações locais?

StrictMode permite relaxar temporariamente uma política. Isso pode ser necessário ao integrar código legado ou uma API da plataforma cuja operação é pequena e inevitável naquele ponto.

```kotlin
val previousPolicy = StrictMode.allowThreadDiskReads()
try {
    legacySdk.initialize(context)
} finally {
    StrictMode.setThreadPolicy(previousPolicy)
}
```

Use esse padrão como **exceção documentada**, não como limpeza de Logcat. Antes de liberar:

- confirme qual operação ocorre;
- meça seu custo em cold start e aparelhos mais lentos;
- procure inicialização assíncrona ou lazy na biblioteca;
- limite o escopo ao menor bloco possível;
- restaure a política em `finally`;
- registre por que a exceção existe e quando poderá ser removida.

Nunca use `permitAll()` globalmente só para esconder violações. Isso desativa justamente a proteção que deveria revelar regressões futuras.

## StrictMode detecta todos os ANRs?

Não. StrictMode encontra categorias conhecidas de comportamento, principalmente violações associadas a políticas de thread e VM. Um app ainda pode ter ANR por:

- deadlock entre threads;
- lock mantido por tempo demais;
- loop ou cálculo pesado na main thread;
- binder call lenta;
- BroadcastReceiver que demora para terminar;
- contenção durante startup;
- excesso de trabalho em callbacks de ciclo de vida;
- renderização ou layout muito custosos.

Por isso, trate StrictMode como uma camada do diagnóstico. Para incidentes reais, analise traces de ANR, vitals da distribuição, logs e reprodução. Para performance repetível, crie cenários com Macrobenchmark. Para startup, revise também [AndroidX App Startup](/blog/androidx-startup-kotlin-inicializacao-app-2026/) e evite inicializar todos os SDKs no `Application.onCreate()`.

## Fluxo recomendado para adotar em um projeto existente

Ativar `detectAll()` com penalidade fatal em um app antigo pode gerar tanto ruído que a equipe desliga a ferramenta. Uma adoção incremental costuma funcionar melhor:

1. habilite `penaltyLog()` apenas em debug;
2. percorra startup, login, home, busca e checkout ou outro fluxo crítico;
3. agrupe violações por causa raiz, não por quantidade de stack traces;
4. corrija primeiro operações de rede e disco em interações críticas;
5. adicione testes para a arquitetura que permitiu o problema;
6. crie exceções locais somente quando justificadas e medidas;
7. repita a auditoria em aparelhos e versões diferentes do Android;
8. torne a política mais rígida em builds internas quando o backlog estiver controlado.

Em pull requests, a revisão deve perguntar onde o trabalho executa, quem controla seu ciclo de vida e como erros/cancelamento são tratados. StrictMode encontra o sintoma em runtime; uma boa arquitetura evita que ele nasça.

## Erros comuns ao usar StrictMode

### Ativar apenas por alguns minutos e nunca mais rodar

Uma tela limpa hoje pode receber amanhã um SDK, uma leitura de preferência ou um fallback síncrono. Mantenha a política na build de debug para que novos fluxos sejam observados continuamente.

### Culpar a última linha do stack trace

A operação de disco pode ocorrer dentro de uma biblioteca, mas seu código decidiu inicializá-la no clique ou no startup. Procure o primeiro ponto sob controle do projeto e entenda o contrato da dependência.

### Mover tudo para `Dispatchers.IO`

Isso evita bloquear a main thread, mas não corrige duplicação, falta de cache, chamadas em excesso ou concorrência sem controle. Uma operação ruim em background continua gastando bateria, dados e recursos.

### Usar `penaltyDeath()` em toda build de desenvolvimento desde o primeiro dia

Se o app tem muitas violações, o time passa a contornar a ferramenta. Comece observando, priorize e aumente o rigor gradualmente.

### Empacotar diagnóstico agressivo em produção

Um usuário não deve perder uma operação importante porque uma penalidade de desenvolvimento encerrou o processo. Se houver motivo para coletar sinais em produção, desenhe uma estratégia própria, com amostragem, privacidade e impacto avaliados.

## Checklist prático

Antes de considerar a configuração pronta, confirme:

- [ ] StrictMode é ativado na `Application` somente para debug ou build interna;
- [ ] `ThreadPolicy` e `VmPolicy` estão configuradas separadamente;
- [ ] o Logcat mostra stack traces úteis;
- [ ] rede nunca é executada na main thread;
- [ ] leituras e escritas de disco saíram dos caminhos críticos de UI;
- [ ] DAOs usam `suspend`, `Flow` ou APIs assíncronas adequadas;
- [ ] recursos fecháveis usam `use` ou contrato explícito de fechamento;
- [ ] exceções locais restauram a política em `finally`;
- [ ] nenhuma correção global usa `permitAll()` para esconder o problema;
- [ ] fluxos críticos são medidos com ferramentas além do StrictMode;
- [ ] a equipe sabe diferenciar aviso de desenvolvimento, jank e ANR real.

## Perguntas frequentes

### StrictMode deve ficar ativo em produção?

Normalmente, não com as mesmas políticas e penalidades usadas em desenvolvimento. `penaltyLog()` pode gerar ruído e `penaltyDeath()` pode prejudicar usuários. O uso mais comum é em debug e builds internas. Se houver uma necessidade específica de observabilidade em produção, avalie uma solução desenhada para esse ambiente.

### StrictMode substitui o Android Profiler?

Não. StrictMode aponta determinadas violações de política e fornece stack traces. O Profiler, traces, Perfetto e Macrobenchmark ajudam a investigar tempo, CPU, memória, frames e execução ao longo do fluxo. As ferramentas se complementam.

### Toda `DiskReadViolation` precisa virar `Dispatchers.IO`?

A causa precisa ser investigada, mas a solução não é mecânica. Você pode remover a leitura, adiá-la, usar cache, trocar a API, inicializar de forma lazy ou movê-la para uma camada assíncrona. Se a operação for inevitável e comprovadamente segura, uma exceção local documentada pode ser aceitável.

### StrictMode encontra memory leaks?

Ele detecta alguns padrões por meio de `VmPolicy`, como determinados recursos fecháveis e componentes vazados. Para investigar cadeias de retenção de objetos, use também [LeakCanary no Android com Kotlin](/blog/leakcanary-android-kotlin-memory-leaks-2026/).

### Por que a violação aparece mesmo dentro de uma coroutine?

Porque uma coroutine não significa automaticamente background thread. `viewModelScope.launch` e `lifecycleScope.launch` usam a main thread por padrão. Para uma chamada bloqueante, a implementação precisa usar uma API assíncrona ou mudar explicitamente para um dispatcher apropriado.

## Conclusão

StrictMode é uma das formas mais baratas de tornar visível o trabalho que sabota a responsividade do Android. Uma configuração pequena em debug consegue revelar disco, rede, recursos abandonados e decisões de inicialização que passariam despercebidas em aparelhos rápidos.

O ganho real aparece quando o time trata cada violação como pista arquitetural. Em vez de silenciar a política, descubra por que uma tela conhece I/O bloqueante, por que um SDK precisa iniciar tão cedo ou por que um recurso não tem dono claro. Depois, valide a correção com métricas e cenários reproduzíveis.

Para continuar a trilha, estude [performance em Kotlin](/guias/guia-kotlin-performance/), [ANRs com Firebase Crashlytics](/blog/firebase-crashlytics-anr-android-kotlin-2026/), [LeakCanary](/blog/leakcanary-android-kotlin-memory-leaks-2026/) e [testes de UI com Espresso](/blog/espresso-android-kotlin-testes-ui-2026/). Se você quer aplicar essas práticas em projetos reais, acompanhe também as [vagas Kotlin e Android](/vagas/) publicadas no Kotlin Brasil.
