Crash dell'app — una terminazione anomala in cui il programma smette di rispondere e si chiude. Nello sviluppo mobile, i crash sono la principale fonte di recensioni negative e calo di valutazione. Secondo Firebase (2024), gli utenti eliminano l'app dopo uno o due crash nel 53% dei casi. Ogni crash riduce la fidelizzazione del 3–5%. I sistemi di monitoraggio come Crashlytics e Sentry aiutano a trovare e correggere rapidamente le cause dei crash prima che colpiscano massicciamente gli utenti.
Punti chiave
Crash — una terminazione imprevista del programma causata da una situazione eccezionale che il codice non ha gestito. Nei sistemi operativi mobili, un crash porta alla chiusura immediata dell'app e mostra una schermata “App arrestata” o torna alla schermata home.
I crash si dividono in due grandi classi. Errori gestiti — i blocchi try/catch catturano l'eccezione, l'app continua a funzionare, possibilmente con perdita di funzionalità. Crash non gestiti — l'eccezione risale fino al livello del sistema operativo e il sistema uccide il processo. Il secondo tipo è particolarmente pericoloso perché l'utente non può salvare i dati.
Un sistema con due milioni di utenti e un tasso di crash dello 0.1% perde 2.000 utenti a ogni rilascio. Secondo Google Play Console (2024), le app con un tasso di crash superiore all'1.5% vengono escluse dai consigli e perdono fino al 30% del traffico organico.
NullPointerException (NPE) — il re dei crash in Java/Kotlin. Tentativo di chiamare un metodo su un oggetto null. In Kotlin, NPE è meno comune grazie alla null safety, ma è ancora possibile quando si utilizza l'operatore !! o si interagisce con codice Java. Google (2024) stima che NPE rappresenti il 25% di tutti i crash delle app Android.
IndexOutOfBoundsException — accesso a un elemento di un elenco con un indice inesistente. Causa comune: i dati arrivano dal server in un formato imprevisto e l'interfaccia tenta di visualizzare una posizione che non esiste. Soluzione — controlla sempre la dimensione della collezione prima di accedere per indice.
ANR (Application Not Responding) — un problema specifico di Android. Il thread dell'interfaccia utente viene bloccato per più di 5 secondi. Cause principali: richieste di rete sul thread principale, calcoli pesanti, sincronizzazione del database. StrictMode in Android aiuta a rilevare il blocco del thread dell'interfaccia durante lo sviluppo.
OutOfMemoryError (OOM) — l'app ha superato il limite di memoria. Su dispositivi mobili con 2–4 GB di RAM, OOM è un problema comune quando si lavora con immagini grandi o elenchi infiniti senza paginazione. Soluzione — Glide/Coil per il caricamento delle immagini, LruCache per la memorizzazione nella cache, ViewHolder in RecyclerView.
Eccezioni di runtime — errori che il compilatore non controlla in fase di compilazione. Si manifestano solo quando il codice viene eseguito su un dispositivo specifico con dati specifici. In Java, sono RuntimeException e le sue sottoclassi: NullPointerException, IllegalArgumentException, ArithmeticException.
Errori fatali (FATAL) — non sono errori di runtime, ma guasti di sistema. Signal 11 (SIGSEGV) — violazione di segmentazione della memoria in codice nativo. Signal 6 (SIGABRT) — terminazione anomala innescata dall'app stessa tramite abort(). Questi crash sono difficili da diagnosticare perché lo stack trace spesso non mostra un contesto chiaro.
In iOS, le cause principali sono NSInvalidArgumentException (nil imprevisto in un parametro) e EXC_BAD_ACCESS (accesso a memoria deallocata). Swift ha ridotto il numero di crash rispetto a Objective-C, ma gli errori nel runtime di ObjC e nelle librerie C causano ancora crash.
Firebase Crashlytics — lo standard per le app mobili. Raccoglie automaticamente gli stack trace, aggiunge log, ID utente e metadati del dispositivo. Raggruppa i crash per firma (classe di errore + riga). Avvisi in tempo reale — notifiche quando il tasso di crash supera una soglia definita (ad esempio, >0.1% all'ora).
Sentry — un'alternativa con capacità più flessibili. Permette di creare contesti personalizzati, aggiungere breadcrumb (eventi precedenti), configurare il filtraggio in-app per escludere errori non importanti. Source maps per Kotlin e Swift permettono di vedere il codice sorgente anziché nomi offuscati.
Best practice per i log: invia i metadati chiave prima di eseguire un'operazione pericolosa — così il log mostrerà cosa stava facendo l'utente prima del crash. Aggiungi chiavi personalizzate (numero di versione dell'API, ultima schermata, dimensione dei dati di input). Questo trasforma uno stack trace inutile in informazioni utilizzabili.
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 — in Kotlin usa `?` per i tipi nullable, `let` e `?:` per la gestione sicura di null. In Swift — optional e guard let. Kotlin moderno (2024) ha aggiunto le annotazioni Contract: `@ContractsDsl` permette di dichiarare che una funzione non restituisce null e il compilatore lo verifica.
Gestione degli errori di rete — ogni richiesta di rete deve gestire timeout, errori di parsing e guasti del server. Retrofit con tipo Result — una classe sealed che garantisce che l'errore venga gestito. Stile senza eccezioni: invece di try/catch, usa Result sealed per la gestione esplicita di successo ed errore.
Feature flags — disattiva funzionalità problematiche da remoto senza rilasciare una nuova versione. Se un'operazione lato server causa un crash su dispositivi vecchi, il flag la disattiva per quel gruppo. Firebase Remote Config permette di modificare il comportamento dell'app senza pubblicare nello store.
Rilascio graduale — pubblica una nuova versione al 5% del pubblico e monitora il tasso di crash. Se il tasso rimane sotto l'obiettivo (di solito <0.1%), espandi al 25%, poi al 50%, poi al 100%. Google Play Console e App Store Connect supportano i rollout graduali per l'arresto automatico al superamento della soglia.
Passo 1: Classificazione — determina la gravità: Critico (crash in >1% degli utenti), Alto (0.1–1%), Medio (<0.1%). Per i crash critici — risposta immediata. Per gli altri — processo standard di correzione bug nello sprint corrente. Google Play Console classifica automaticamente i crash in base al numero di utenti interessati.
Passo 2: Analisi dello stack trace — apri il log in Crashlytics, controlla la posizione esatta del crash. Verifica le chiavi personalizzate: quale schermata, quali dati, versione del sistema operativo. Correla con l'ultimo deployment — spesso un crash è causato da una modifica recente del codice che ha influenzato uno scenario d'uso imprevisto.
Passo 3: Riproduzione — prova a riprodurre il crash su un dispositivo o emulatore con parametri simili. Se non ci riesci, controlla il log di crash per modelli: modelli specifici (Samsung A10), versioni Android (API < 26), impostazioni locali. Soluzione — aggiungi una condizione difensiva che copra lo scenario.
Passo 4: Correzione e monitoraggio — pubblica un hotfix prioritario. Dopo il rilascio, assicurati che il tasso di crash per questo tipo scenda a zero. Scrivi un test di regressione che copra lo scenario di crash. Senza un test, lo stesso bug potrebbe ripresentarsi nel prossimo refactoring.
Domande frequenti
Tasso di crash normale — inferiore allo 0.1% per i rilasci di produzione. Google Play raccomanda di mantenere il tasso di crash al di sotto dell'1.5%, ma le app migliori (YouTube, Instagram) mantengono lo 0.01–0.05%. Per i rilasci con nuove funzionalità, è consentito un aumento temporaneo fino allo 0.5% con successiva riduzione dopo un hotfix.
Crash — l'app termina in modo anomalo. ANR (Application Not Responding) — l'app si blocca per più di 5 secondi ma non si chiude forzatamente. L'utente vede un dialogo “App non risponde” e può attendere o chiudere. I problemi ANR non sono meno gravi dei crash e influiscono anche sulla valutazione nello store.
Dispositivi diversi hanno versioni del sistema operativo diverse, quantità di memoria, versioni delle librerie e persino processori diversi. Esempio: un crash su Android 6 (API 23) dovuto alla mancanza di un'autorizzazione in fase di esecuzione potrebbe non verificarsi su Android 12. Analizza il log di crash per filtri: versione del sistema operativo, modello del dispositivo, quantità di RAM. Questo indicherà la specificità del problema.
Aggiungi breadcrumb personalizzati in Crashlytics: registra gli eventi chiave prima di eseguire un'operazione. Se il crash si verifica al passo 3 dell'onboarding, questo indica un problema in una schermata specifica. Simboli di debug (dSYM, ProGuard mapping) — caricali sempre in Crashlytics per vedere i nomi reali delle funzioni anziché quelli offuscati.
In produzione — mai. Un crash non gestito peggiora l'esperienza utente. Usa try/catch con registrazione dell'errore. In modalità debug, il crash è accettabile per un feedback rapido allo sviluppatore. Le asserzioni — per verificare invarianti che non dovrebbero mai essere violate, ma solo nelle build di debug.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche