Congelamentos no desenvolvimento — essência, causas e prevenção

Autor: IT Sectr Publicado: 2026-07-28 Tempo de leitura: 9 min

Congelamento (travamento) é um estado no qual um aplicativo móvel para de responder a qualquer ação do usuário por um período prolongado. Ao contrário de lags (lentidão) e glitches (comportamento incorreto), o congelamento bloqueia completamente a UI: toques não são processados, a animação para, a tela “congela”. A causa é o bloqueio da thread principal por uma operação síncrona, um deadlock em código multithread ou uma coleta de lixo anormalmente longa. De acordo com a Documentação do Apple Main Thread Checker, mais de 40% dos relatórios de crash no iOS estão relacionados ao bloqueio da thread principal. No Android, uma situação semelhante leva ao ANR — o diálogo do sistema “O aplicativo não responde”.

Principais pontos

  • Congelamento — bloqueio completo da UI por um período prolongado (segundos a dezenas de segundos), diferente de lags e glitches
  • Causas principais — bloqueio da thread principal por E/S, deadlock entre threads, loop infinito e vazamento de memória com GC longo
  • Diagnóstico inclui Main Thread Checker no iOS, logs ANR /data/anr/traces.txt no Android e análise de dump de threads
  • Eliminação — descarregar todas as operações potencialmente longas para threads em segundo plano, usar Structured Concurrency e evitar synchronized na thread de UI
  • Prevenção — StrictMode, Main Thread Checker no esquema Debug, análise estática para deadlock e testes periódicos medindo tempo de resposta

O que é um congelamento no desenvolvimento móvel

Congelamento (travamento) em um aplicativo móvel é um estado no qual o aplicativo para de processar eventos de entrada e atualizar a interface por vários segundos ou mais. Tecnicamente, isso significa que a thread principal está bloqueada e não pode executar a próxima iteração do loop de execução.

Diferença entre congelamento, lag e ANR

Um lag é um atraso de até 500 ms no qual o usuário nota lentidão, mas o aplicativo continua funcionando. Congelamento dura de 1 segundo a dezenas de segundos. ANR no Android é um caso especial de congelamento que durou mais de 5 segundos e foi detectado pelo sistema. Nem todo congelamento leva a ANR, mas todo ANR é um congelamento documentado pelo sistema.

Consequências dos congelamentos

No Android, um congelamento com mais de 5 segundos provoca um diálogo ANR oferecendo fechar o aplicativo. No iOS, o sistema tem um watchdog — se o aplicativo não responder a eventos por 10–20 segundos, o Watchdog encerra o processo com o código 0x8badf00d (ate bad food). O usuário vê apenas o aplicativo fechando repentinamente para a tela inicial.

Causas de congelamentos no Android e iOS

Qualquer operação que leve mais de 100 ms e seja executada na thread principal pode potencialmente causar um congelamento. Vamos ver as principais fontes de bloqueios.

E/S síncrona na thread de UI

Ler um arquivo grande, uma requisição de rede sem assincronia, salvar dados no SharedPreferences com o método síncrono apply seguido de commit — todas essas operações bloqueiam a thread principal. No Android, ler sincronamente um arquivo de 10 MB pode levar 200–500 ms dependendo da velocidade da memória flash. No iOS, um carregamento síncrono de URLSession sem completionHandler bloqueia a UI pelo tempo de resposta do servidor.

Deadlock em código multithread

Quando duas threads esperam por recursos retidos uma pela outra, ocorre um deadlock. Em aplicativos móveis, um cenário típico é a thread A bloquear Lock1 e esperar Lock2, enquanto a thread B bloqueia Lock2 e espera Lock1. Ambas as threads congelam para sempre. Se uma delas for a thread principal, o aplicativo congela completamente.

Loop infinito ou recursão

Um erro de lógica — por exemplo, while(true) sem condição de saída ou recursão sem caso base — leva à execução infinita na thread principal. O Android detecta isso via ANR após 5 segundos, o iOS — via Stackshot, que captura uma pilha de chamadas infinitamente repetida.

  • Android — Cursor sem fechar, requisição síncrona via execute() em vez de enqueue(), FileInputStream.read() na thread de UI
  • iOS — performSelectorOnMainThread:withObject:waitUntilDone:YES, lançar NSURLConnection sendSynchronousRequest, carregar imagem com dataWithContentsOfURL
  • Multiplataforma — Flutter compute sem um isolate dedicado, React Native NativeModule síncrono

Como diagnosticar congelamentos

O diagnóstico de congelamentos requer ferramentas capazes de capturar o estado de todas as threads no momento do bloqueio.

Logs ANR no Android

Em cada ANR, o sistema Android salva um arquivo /data/anr/traces.txt contendo um dump de pilha de cada thread do aplicativo. Analisar este arquivo é o principal método de diagnóstico: encontre a thread main e veja em qual método ela parou. Se a pilha terminar em Thread.sleep, InputStream.read ou Lock.lock — a causa foi encontrada.

Stackshot no iOS

O Xcode pode capturar um Stackshot — uma snapshot das pilhas de todas as threads — quando o aplicativo congela (sinal SIGSTOP). Ative “Logging” → “Include Stackshot Logs” no esquema. Em um crash com código 0x8badf00d, extraia o log de crash de Devices & Simulators e encontre a thread com.apple.main-thread com a pilha travada.

Main Thread Checker no Xcode

O Main Thread Checker detecta automaticamente chamadas UIKit de threads em segundo plano enquanto o aplicativo está em execução. Ative-o no esquema (Diagnostics → Main Thread Checker). Cada aviso é uma causa potencial de congelamento, especialmente se ocorrer em um closure completionHandler de uma requisição de rede.

Exemplo de detecção de bloqueios via StrictMode no Android:

kotlin
class App : Application() {
    override fun onCreate() {
        super.onCreate()
        StrictMode.setVmPolicy(StrictMode.VmPolicy.Builder()
            .detectLeakedSqlLiteObjects()
            .detectLeakedClosableObjects()
            .penaltyLog()
            .build())
        StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.Builder()
            .detectAll()
            .penaltyDeath()
            .build())
    }
}

Métodos para eliminar bloqueios de UI

Eliminar congelamentos começa com a movimentação de todas as operações potencialmente longas para threads em segundo plano. Vamos ver técnicas específicas para cada plataforma.

Concorrência estruturada com corrotinas

O Kotlin Coroutines com viewModelScope.launch(Dispatchers.IO) garante que operações de rede ou leituras de banco de dados sejam executadas em uma thread em segundo plano. Dispatchers.Main é usado apenas para atualizações de UI. Importante: todas as funções suspend devem ser estruturadas — as corrotinas filhas são canceladas quando a pai é cancelada, prevenindo vazamentos de threads.

Filas assíncronas no iOS

O Grand Central Dispatch com DispatchQueue.global(qos: .userInitiated) para tarefas em segundo plano e DispatchQueue.main.async para atualizações de UI é o padrão. Evite sync() na fila principal — isso é um deadlock garantido. Use async/await (Swift 5.5+) para código assíncrono mais legível com retorno automático à thread principal via MainActor.

Evitando synchronized na thread de UI

Blocos synchronized em Kotlin e @synchronized em Swift na thread principal são perigosos: se outra thread já adquiriu este bloqueio, a thread principal vai travar esperando. Use tipos atômicos (AtomicInteger, propriedades atômicas em Swift) ou filas seriais em vez de bloqueios.

Exemplo de carregamento assíncrono de dados com corrotinas no Android:

kotlin
class DataViewModel : ViewModel() {
    private val _data = MutableStateFlow<List<Item>>(emptyList())
    val data: StateFlow<List<Item>> = _data.asStateFlow()

    fun loadData() {
        viewModelScope.launch(Dispatchers.IO) {
            val result = fetchFromNetwork()
            _data.emit(result)
        }
    }
}

Prevenção de congelamentos na fase de desenvolvimento

Uma combinação de ferramentas, princípios arquitetônicos e processos de revisão de código ajuda a prevenir sistematicamente congelamentos.

StrictMode com penaltyDeath

Configure o StrictMode com penaltyDeath para as políticas de thread — isso causará uma falha imediata do aplicativo quando uma chamada de rede ou E/S de disco for detectada na thread principal. O desenvolvedor não poderá ignorar o problema. Em builds de produção, use penaltyLog para coletar estatísticas sem falhas.

Main Thread Checker no esquema Debug

No iOS, ative o Main Thread Checker no esquema Debug e configure a CI para executar testes com esta opção. Se um teste contiver uma chamada UIKit de uma thread em segundo plano — ele deve falhar. Esta é a única maneira confiável de identificar o problema antes de enviar para o TestFlight.

Revisão por pares com verificação de multithreading

Adicione um item obrigatório ao processo de revisão de código: verificar se qualquer chamada de rede, operação de arquivo, acesso a banco de dados ou cálculo pesado é executado em uma thread em segundo plano. O Deadlock pode ser detectado com analisadores estáticos: o Infer do Facebook e o Thread Safety Checker do Xcode encontram bloqueios potenciais antes da execução.

  • Android — StrictMode, Infer, Android Lint Multithread, Kotlin Coroutines com viewModelScope
  • iOS — Main Thread Checker, TSAN (Thread Sanitizer), Xcode Analyze, Swift async/await com MainActor
  • Multiplataforma — Flutter compute isolate, React Native interaction manager com requestAnimationFrame

Perguntas frequentes

Qual é a diferença entre congelamento e ANR?

ANR (Application Not Responding) é uma notificação do sistema Android que aparece quando a thread principal congela por mais de 5 segundos. Congelamento é um conceito mais amplo: qualquer bloqueio de UI de qualquer duração. O iOS não tem ANR, mas tem um Watchdog com tempo limite de 10–20 segundos.

Como ler traces.txt no Android?

O arquivo está localizado em /data/anr/traces.txt. O acesso requer acesso root ou adb shell: execute adb shell cat /data/anr/traces.txt \> traces.txt com privilégios root. Na pilha, encontre a thread “main” — o último método chamado indica a causa do bloqueio.

Por que o aplicativo congela no iOS, mas não trava?

Se o congelamento durar menos de 10 segundos, o Watchdog não é acionado e o aplicativo simplesmente “trava” até que a operação bloqueante seja concluída. O usuário não vê um crash, mas sente frustração. Para detectar tais casos, use o MetricKit com traces personalizados de tempo de execução.

Como testar o aplicativo contra congelamentos?

Use testes de UI com verificação de que a tela abre em < 1 segundo. Adicione à CI a medição do tempo entre o toque e o aparecimento da próxima tela. No Android, use Espresso com IdlingResource para aguardar operações assíncronas. No iOS, use XCTest com XCTWaiter para verificar o tempo de carregamento.

O SwiftUI pode causar congelamentos?

O SwiftUI por si só não causa congelamentos, mas cálculos complexos na propriedade body sim. Se o body levar 500 ms para calcular devido a operações pesadas, a UI congela. A solução é descarregar os cálculos para Task.detached e atualizar @State de forma assíncrona no ator principal.

Resumo

  • Congelamento — bloqueio completo da UI por segundos a dezenas de segundos, causado por bloqueio da thread principal, deadlock ou loop infinito
  • Diagnóstico — /data/anr/traces.txt no Android, Stackshot e Main Thread Checker no iOS
  • Causas principais — E/S síncrona, deadlock entre threads, recursão infinita, GC longo
  • Eliminação — corrotinas com dispatchers corretos, async/await com MainActor, descarregar todas as operações de E/S para threads em segundo plano
  • Prevenção — StrictMode com penaltyDeath, Main Thread Checker, análise estática para deadlock (Infer, TSAN)
  • No Android congelamento > 5 s = ANR; no iOS > 10–20 s = Watchdog crash (0x8badf00d)
  • Recomendação: ative o Thread Sanitizer no esquema Debug e configure a CI para executar testes com TSAN para detectar data races e deadlocks

Vamos desenvolver um aplicativo móvel chave na mão

A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.

Discutir o projeto

Leia também