Crash do aplicativo — uma terminação anormal na qual o programa para de responder e fecha. No desenvolvimento móvel, os crashes são a principal fonte de avaliações negativas e queda de classificação. De acordo com o Firebase (2024), os usuários excluem o aplicativo após um ou dois crashes em 53% dos casos. Cada crash reduz a retenção em 3–5%. Sistemas de monitoramento como Crashlytics e Sentry ajudam a encontrar e corrigir rapidamente as causas de quedas antes que afetem muitos usuários.
Principais conclusões
Crash — um encerramento inesperado do programa causado por uma situação excepcional que o código não tratou. Em sistemas operacionais móveis, um crash leva ao fechamento imediato do aplicativo e mostra uma tela “Aplicativo parou” ou retorna à tela inicial.
Os crashes são divididos em duas grandes classes. Erros tratados — blocos try/catch capturam a exceção, o aplicativo continua funcionando, possivelmente com perda de funcionalidade. Crashes não tratados — a exceção sobe até o nível do sistema operacional e o sistema mata o processo. O segundo tipo é especialmente perigoso porque o usuário não pode salvar dados.
Um sistema com dois milhões de usuários e uma taxa de crash de 0,1% perde 2.000 usuários em cada lançamento. De acordo com o Google Play Console (2024), aplicativos com taxa de crash acima de 1,5% são excluídos das recomendações e perdem até 30% do tráfego orgânico.
NullPointerException (NPE) — o rei dos crashes em Java/Kotlin. Tentativa de chamar um método em um objeto null. Em Kotlin, NPE é menos comum graças ao null safety, mas ainda é possível ao usar o operador !! ou interagir com código Java. O Google (2024) estima que NPE representa 25% de todos os crashes de aplicativos Android.
IndexOutOfBoundsException — acesso a um elemento de lista com um índice inexistente. Causa comum: os dados chegam do servidor em um formato inesperado e a interface tenta exibir uma posição que não existe. Solução — sempre verifique o tamanho da coleção antes de acessar por índice.
ANR (Application Not Responding) — um problema específico do Android. A thread da interface do usuário fica bloqueada por mais de 5 segundos. Principais causas: requisições de rede na thread principal, cálculos pesados, sincronização com banco de dados. StrictMode no Android ajuda a detectar bloqueio da thread da interface durante o desenvolvimento.
OutOfMemoryError (OOM) — o aplicativo excedeu o limite de memória. Em dispositivos móveis com 2–4 GB de RAM, OOM é um problema comum ao trabalhar com imagens grandes ou listas infinitas sem paginação. Solução — Glide/Coil para carregar imagens, LruCache para cache, ViewHolder no RecyclerView.
Exceções de tempo de execução — erros que o compilador não verifica durante a compilação. Eles só aparecem quando o código é executado em um dispositivo específico com dados específicos. Em Java, são RuntimeException e suas subclasses: NullPointerException, IllegalArgumentException, ArithmeticException.
Erros fatais (FATAL) — não são de execução, mas falhas do sistema. Signal 11 (SIGSEGV) — violação de segmentação de memória em código nativo. Signal 6 (SIGABRT) — terminação anormal causada pelo próprio aplicativo via abort(). Esses crashes são difíceis de diagnosticar porque o stack trace geralmente não mostra um contexto claro.
No iOS, as principais causas são NSInvalidArgumentException (nil inesperado em um parâmetro) e EXC_BAD_ACCESS (acesso a memória liberada). O Swift reduziu o número de crashes em comparação com o Objective-C, mas erros no runtime do ObjC e bibliotecas C ainda causam quedas.
Firebase Crashlytics — o padrão para aplicativos móveis. Coleta automaticamente stack traces, adiciona logs, IDs de usuário e metadados do dispositivo. Agrupa crashes por assinatura (classe de erro + linha). Alertas em tempo real — notificações quando a taxa de crash excede um limite definido (por exemplo, >0,1% por hora).
Sentry — uma alternativa com capacidades mais flexíveis. Permite criar contextos personalizados, adicionar breadcrumbs (eventos anteriores), configurar filtragem no aplicativo para excluir erros sem importância. Source maps para Kotlin e Swift permitem ver o código fonte em vez de nomes ofuscados.
Melhores práticas para logs: envie metadados chave antes de realizar uma operação perigosa — assim o log mostrará o que o usuário estava fazendo antes do crash. Adicione chaves personalizadas (número da versão da API, última tela, tamanho dos dados de entrada). Isso transforma um stack trace inútil em informação acionável.
class PaymentViewModel : ViewModel() {
fun processPayment(amount: Double) {
crashlytics.setCustomKey("last_screen", "payment")
crashlytics.setCustomKey("amount", amount)
try {
api.charge(amount)
} catch (e: Exception) {
crashlytics.recordException(e)
}
}
}
Optional binding e null safety — em Kotlin use `?` para tipos anuláveis, `let` e `?:` para tratamento seguro de null. Em Swift — optionals e guard let. Kotlin moderno (2024) adicionou anotações Contract: `@ContractsDsl` permite declarar que uma função não retorna null, e o compilador verifica isso.
Tratamento de erros em rede — cada requisição de rede deve tratar timeout, erros de análise e falhas do servidor. Retrofit com tipo Result — uma classe selada que garante que o erro será tratado. Estilo sem exceção: em vez de try/catch, use Result selado para tratamento explícito de sucesso e erro.
Feature flags — desative funcionalidades problemáticas remotamente sem lançar uma nova versão. Se uma operação do servidor causar um crash em dispositivos antigos, a flag a desativa para esse grupo. Firebase Remote Config permite alterar o comportamento do aplicativo sem publicar na loja.
Lançamento gradual — lance uma nova versão para 5% da audiência e monitore a taxa de crash. Se a taxa permanecer abaixo da meta (geralmente <0,1%), expanda para 25%, depois 50%, depois 100%. Google Play Console e App Store Connect suportam lançamentos em estágios para parada automática quando o limite é excedido.
Passo 1: Classificação — determine a gravidade: Crítico (crash em >1% dos usuários), Alto (0,1–1%), Médio (<0,1%). Para crashes críticos — resposta imediata. Para os demais — processo padrão de correção de bugs no sprint atual. Google Play Console classifica automaticamente os crashes pelo número de usuários afetados.
Passo 2: Análise do stack trace — abra o log no Crashlytics, veja o local exato da queda. Verifique as chaves personalizadas: qual tela, quais dados, versão do SO. Correlacione com a última implantação — frequentemente um crash é causado por uma alteração recente no código que afetou um cenário de uso inesperado.
Passo 3: Reprodução — tente reproduzir o crash em um dispositivo ou emulador com parâmetros semelhantes. Se não conseguir, verifique o log de crash em busca de padrões: modelos específicos (Samsung A10), versões do Android (API < 26), locales. Solução — adicione uma condição defensiva que cubra o cenário.
Passo 4: Correção e monitoramento — lance um hotfix com prioridade. Após o lançamento, certifique-se de que a taxa de crash para este tipo caia para zero. Escreva um teste de regressão que cubra o cenário do crash. Sem um teste, o mesmo bug pode retornar na próxima refatoração.
Perguntas frequentes
Taxa de crash normal — abaixo de 0,1% para lançamentos de produção. O Google Play recomenda manter a taxa de crash abaixo de 1,5%, mas os principais aplicativos (YouTube, Instagram) mantêm 0,01–0,05%. Para lançamentos com nova funcionalidade, é aceitável um aumento temporário para 0,5% com redução subsequente após um hotfix.
Crash — o aplicativo termina de forma anormal. ANR (Application Not Responding) — o aplicativo congela por mais de 5 segundos, mas não fecha à força. O usuário vê um diálogo “Aplicativo não responde” e pode esperar ou fechar. Os problemas de ANR não são menos graves que os crashes e também afetam a classificação na loja.
Diferentes dispositivos têm diferentes versões do SO, quantidade de memória, versões de bibliotecas e até processadores. Exemplo: um crash no Android 6 (API 23) devido à falta de uma permissão em tempo de execução pode não ocorrer no Android 12. Analise o log de crash por filtros: versão do SO, modelo do dispositivo, quantidade de RAM. Isso indicará a especificidade do problema.
Adicione breadcrumbs personalizados no Crashlytics: registre eventos chave antes de realizar uma operação. Se o crash ocorrer na etapa 3 da integração, isso aponta para um problema em uma tela específica. Símbolos de depuração (dSYM, ProGuard mapping) — sempre faça upload deles para o Crashlytics para ver os nomes reais das funções em vez de ofuscados.
Em produção — nunca. Um crash não tratado piora a experiência do usuário. Use try/catch com registro de erro. No modo de depuração, causar um crash é aceitável para feedback rápido ao desenvolvedor. Assertions — para verificar invariantes que nunca devem ser violados, mas apenas em compilações de depuração.
Resumo
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.
Leia também