Crash no desenvolvimento mobile: o que é, tipos e métodos de prevenção

Autor: IT Sectr Publicado: 2026-03-29 Tempo de leitura: 9 min

Crash é uma terminação anormal de um aplicativo móvel devido a uma exceção não tratada ou falha fatal do sistema. De acordo com o Firebase Crashlytics, cerca de 2% dos usuários enfrentam crashes diariamente, e cada queda reduz a retenção em 10–20%. Compreender as causas e métodos de prevenção de crashes é uma habilidade essencial para qualquer desenvolvedor mobile.

Principais pontos

  • Crash — uma exceção não tratada que leva à terminação anormal do processo
  • NullPointerException — o tipo de crash mais comum em aplicações Java/Kotlin
  • Crash reporters coletam stack trace, estado do dispositivo e dados do usuário
  • Firebase Crashlytics — a ferramenta padrão para monitoramento de crashes no desenvolvimento mobile
  • Prevenção inclui tratamento adequado de erros, testes e verificação de null-safety

O que é um Crash

Crash é uma terminação anormal de uma aplicação causada por uma exceção não tratada ou um sinal fatal do sistema que não foi tratado no código da aplicação. Quando o sistema ou a máquina virtual (JVM, ART) detecta uma condição fatal — NullPointerException, IndexOutOfBoundsException, OutOfMemoryError — ela interrompe imediatamente o processo e o descarrega da memória. O usuário vê um fechamento repentino do aplicativo sem qualquer notificação de erro do sistema. De acordo com o Google, aplicativos com uma taxa livre de crashes abaixo de 99% perdem até 20% dos usuários ativos por mês.

No Android, o mecanismo de tratamento de crashes difere dos sistemas desktop. Em vez de um diálogo de depuração com stack trace, o Android simplesmente encerra o processo sem salvar informações detalhadas. A coleta de informações sobre crashes é tarefa de bibliotecas de terceiros (Crashlytics, Sentry, Bugsnag) que interceptam exceções através do Thread.setDefaultUncaughtExceptionHandler antes que o processo seja encerrado.

iOS usa um mecanismo semelhante com NSException e Mach exceptions para lidar com erros fatais. Quando uma exceção não tratada ocorre, o sistema encerra o aplicativo e o relatório é salvo como um arquivo .crash. A coleta de crashes no iOS requer integração com o Crashlytics ou o relatório embutido através do Xcode Organizer.

Principais tipos de crashes

Cinco categorias de crashes cobrem 90% de todas as falhas em aplicativos móveis. Compreender cada tipo ajuda a diagnosticar e corrigir problemas em produção mais rapidamente.

NullPointerException — o rei dos crashes

NullPointerException (NPE) é o tipo de crash mais comum em todas as aplicações Java/Kotlin. Ocorre ao tentar chamar um método ou acessar um campo de um objeto que é null. Cenários típicos: campo de Activity não inicializado ao girar a tela, resposta null do servidor durante a desserialização JSON, navegação descuidada pelo adaptador do RecyclerView.

Kotlin resolve o problema do NPE no nível da linguagem através de tipos null-safe: String? não pode ser usado sem verificação explícita. No entanto, a compatibilidade com Java e Reflection ainda criam riscos. Use as anotações @NonNull e @Nullable e ative strictNullChecks nas ferramentas de análise estática.

kotlin
fun safeLength(text: String?): Int {
    return text?.length ?: 0 // manipulação segura de null
}

IndexOutOfBoundsException e erros de coleção

IndexOutOfBoundsException ocorre ao acessar um índice inexistente de uma lista ou array. Cenários comuns: remoção de um elemento do RecyclerView sem sincronização com o adaptador, modificação de ArrayList em múltiplas threads sem bloqueio, cálculo incorreto de posição no ViewPager. ConcurrentModificationException é um parente próximo ao iterar e modificar coleções simultaneamente.

Use CopyOnWriteArrayList para acesso multi-thread ou coleções Lock-free do java.util.concurrent. Para sincronização com a UI, use o DiffUtil que calcula a diferença entre listas antiga e nova de forma segura e eficiente.

ClassCastException — problemas de tipo

ClassCastException ocorre ao converter um objeto para um tipo incompatível. No Android, as causas típicas são: tipo incorreto de ViewHolder no RecyclerView (diferentes tipos de célula sem getItemViewType adequado), conversão incorreta de Fragment durante a navegação, objetos Serializable com diferentes versões de classe.

Use o safe-cast do Kotlin através do operador as?, que retorna null em caso de incompatibilidade de tipos. Em Java — verifique com instanceof antes de converter. Para objetos Parcelable, declare sempre CREATOR em cada classe.

IllegalStateException e erros lógicos

IllegalStateException sinaliza a chamada de um método em um estado inadequado do objeto. Um exemplo típico no Android — getSupportFragmentManager() após onSaveInstanceState, quando o commit() de um fragment não é permitido. Outro caso comum — chamar dismiss() em um diálogo já fechado.

Verifique o estado do ciclo de vida antes de operações com o FragmentManager. Use commitAllowingStateLoss() apenas quando tiver certeza de que a perda de estado não é crítica. No Kotlin, crie builders semelhantes a DSL que eliminam estados inválidos no nível de tipos.

Native Crash (sinais SIGSEGV, SIGABRT)

Native Crash ocorre em código nativo C/C++ devido a violações de memória: desreferência de ponteiro nulo, double-free, estouro de buffer de pilha. No Android, esses crashes ocorrem em bibliotecas NDK, motores de jogo (Unity, Unreal) e dependências do sistema. O Native Crash NÃO é interceptado pelo Thread.setDefaultUncaughtExceptionHandler — ele encerra o processo instantaneamente.

Para diagnosticar crashes nativos, use arquivos minidump (Breakpad) ou tombstones do Android. O Firebase Crashlytics suporta a coleta de crashes nativos através do NDK SDK. No iOS, um problema similar é resolvido usando PLCrashReporter.

Ferramentas de crash reporting

Três ferramentas dominam o mercado de crash reporting móvel. Cada uma fornece coleta de stack trace, agregação por versão do aplicativo e notificações de novos crashes.

Firebase Crashlytics

Crashlytics é o crash reporter mais popular para aplicativos móveis, parte do ecossistema Firebase. Ele coleta automaticamente stack traces, informações do dispositivo, versão do SO e chaves personalizadas do usuário. A integração leva 10 minutos através do Firebase Console e do Gradle Plugin. O Crashlytics também suporta logs em tempo real (Logcat) e rastreamentos de usuário.

kotlin
FirebaseCrashlytics.getInstance()
    .setCustomKey("current_screen", "ProfileFragment")

FirebaseCrashlytics.getInstance()
    .log("User tapped login button")

Sentry

Sentry é uma alternativa ao Crashlytics com um sistema de filtragem mais flexível e suporte para mais de 90 plataformas. Diferente do Firebase, o Sentry fornece um servidor auto-hospedado (self-hosted) para empresas com requisitos rigorosos de dados. O Sentry suporta rastreamento distributivo, breadcrumbs e integração com pipelines CI/CD.

Bugsnag e AppCenter

Bugsnag se destaca pelo suporte a alertas baseados em severidade: classifica os crashes em critical, error e warning. AppCenter da Microsoft é uma ferramenta gratuita com funcionalidade básica para projetos pequenos. Ambos suportam Android, iOS, React Native e Flutter.

Como analisar um crash

A análise de um crash é o processo de reconstruir a imagem completa do ocorrido. O stack trace mostra apenas o último ponto de falha, mas não fornece o contexto que levou ao problema. Uma abordagem profissional inclui quatro etapas.

A primeira etapa é ler o stack trace. Identifique a classe, o método e a linha de código onde a exceção ocorreu. Siga a cadeia de chamadas do quadro superior ao inferior: a última linha na pilha é o local do crash, e as linhas superiores são a sequência de chamadas. A desofuscação (mapeamento ProGuard/R8) é obrigatória para builds de produção.

A segunda etapa é o contexto do dispositivo. O Crashlytics mostra o modelo do dispositivo, a versão do SO, a memória disponível e a versão do aplicativo. Por exemplo, um crash apenas no Samsung Galaxy S10 com Android 11 aponta para um problema com uma versão específica do One UI, não um erro geral de código.

A terceira etapa é a reprodução em um dispositivo de teste. Se o crash não reproduz consistentemente, peça ao usuário os passos exatos ou use o Remote Config para registrar antes da seção problemática de código. O teste AB da correção em parte da audiência ajuda a confirmar a solução.

A quarta etapa é o monitoramento pós-correção. Após publicar a correção, monitore a taxa de crashes por 3–5 dias. Se o crash desaparecer completamente — a correção funcionou. Se a frequência diminuiu mas não chegou a zero — existe um segundo cenário que requer análise separada.

Práticas de prevenção de crashes

Uma abordagem sistemática para a prevenção de crashes inclui ferramentas de análise estática, testes obrigatórios de casos limite e tratamento adequado de erros em todos os níveis da aplicação.

Análise estática de código

Detekt (Kotlin) e Lint (Android) encontram problemas potenciais em tempo de compilação: variáveis não utilizadas, NPEs potenciais, uso incorreto de API. Inclua essas ferramentas no pipeline de CI com um limite de erros. Por exemplo, Detekt com uma configuração de 30+ avisos ou qualquer bloqueio de erro não passa na compilação.

Testes unitários e testes de UI

A cobertura de cenários-chave de uso com testes unitários é a proteção básica contra crashes de regressão. Teste modelos de dados, ViewModel e camadas de UseCase com casos limite: valores null, listas vazias, JSON inválido. Testes de UI via Espresso ou Compose Test cobrem fluxos críticos: autenticação, pagamento, onboarding.

Degradação gradual

Projete a aplicação de modo que uma falha em um módulo não derrube a tela inteira. Use blocos catch no nível do ViewModel com estado de fallback: mostrar um placeholder em vez de uma lista, dados em cache quando offline, uma imagem de fallback ao carregar. Isso transforma um crash potencial em um cenário de UX controlado.

Rollout gradual com monitoramento

Lançamentos escalonados são uma prática padrão na Google Play e App Store: uma nova versão é distribuída para 5%, depois 20% e depois 100% do público com intervalos de 1–3 dias. Em cada etapa, a taxa de crashes é monitorada: se a taxa livre de crashes cair abaixo de 99,5%, o rollout para automaticamente. O Firebase Remote Config permite desativar funcionalidades problemáticas sem publicar uma nova versão.

Controle de versão de dependências

Renovate ou Dependabot no CI verificam automaticamente bibliotecas por vulnerabilidades conhecidas e bugs críticos. Atualizar uma única dependência pode eliminar uma classe inteira de crashes. No entanto, teste as atualizações no ambiente de staging antes de lançar em produção — uma nova versão de biblioteca pode conter mudanças incompatíveis.

Perguntas frequentes

É possível prevenir 100% dos crashes?

Não. Alguns crashes são causados por fatores fora do controle do desenvolvedor: erros do sistema, problemas de hardware, incompatibilidade de firmware. O objetivo é reduzir a taxa para 0,1% ou menos e minimizar o tempo de resposta para os crashes restantes.

Como um crash reporter difere da analítica?

Um crash reporter coleta stack trace, estado da memória e informações do dispositivo no momento do crash. A analítica coleta dados comportamentais do usuário. O Crashlytics combina ambas as abordagens, fornecendo contexto do crash juntamente com chaves personalizadas do usuário.

Por que o stack trace está ofuscado?

ProGuard e R8 ofuscam o código para proteger a propriedade intelectual. Para desofuscação, carregue o arquivo de mapeamento no Crashlytics durante a publicação. Sem um arquivo de mapeamento, o stack trace mostrará a.a(), b.b() em vez dos nomes reais de classes e métodos.

Como um crash reporter intercepta exceções?

Através do Thread.setDefaultUncaughtExceptionHandler no Android: a biblioteca registra seu próprio manipulador, que recebe a exceção não tratada primeiro, salva os dados e só então encerra o processo. No iOS, usa-se NSSetUncaughtExceptionHandler para NSException e Mach exception handler para sinais.

O que são crashes fatais e não fatais?

Fatal — o aplicativo foi encerrado. Não fatal (exceção capturada) — o desenvolvedor capturou a exceção via try-catch, mas pode indicar um problema potencial. O Crashlytics distingue esses tipos e permite filtrar não fatais separadamente para não poluir o dashboard.

Resumo

  • Crash — terminação anormal da aplicação devido a uma exceção não tratada ou sinal fatal
  • NullPointerException continua sendo o tipo de crash mais comum em aplicativos móveis
  • Firebase Crashlytics — a ferramenta padrão para coletar e analisar crashes em produção
  • Análise de crash inclui leitura do stack trace, contexto do dispositivo e reprodução em ambiente de teste
  • Análise estática (Detekt, Lint) previne alguns crashes em tempo de compilação
  • Degradação gradual transforma crashes potenciais em cenários gerenciáveis com dados de fallback
  • Arquivos de mapeamento são obrigatórios para desofuscar stack traces em builds de produção

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