Prestazioni nello sviluppo mobile: cosa sono, quali metriche e come migliorarle

Autore: IT Sectr Pubblicato: 2026-03-25 Tempo di lettura: 12 min

Un'app lenta è la ragione principale per cui gli utenti disinstallano i programmi. Millisecondi di ritardo all'avvio o durante lo scorrimento di un elenco riducono la retention di decine di punti percentuali. Le prestazioni (performance) non sono solo velocità, ma anche stabilità: assenza di ANR, crash e perdite di memoria. Questo articolo copre tutti gli aspetti delle prestazioni: dalla gestione della memoria (GC, ARC) al profiling con strumenti. Scopri di più nella guida ufficiale Android Performance.

Punti chiave

  • ANR e Crash sono i principali nemici dell'esperienza utente; prevenuti con thread in background
  • Perdita di memoria e Retain Cycle portano a crash OOM; risolti con riferimenti deboli e utility
  • GC (Android) e ARC (iOS) — modelli di gestione della memoria; comprenderne il funzionamento è critico
  • Profiling (Instruments, Android Profiler, LeakCanary) — fase di sviluppo obbligatoria
  • Cold Start — la metrica di avvio più importante; ottimizzare Application.onCreate e inizializzazione pigra
  • Dimensione dell'App — utilizzare App Bundle, R8, VectorDrawable e WebP per ridurre le dimensioni

Perché l'app è lenta?

Le prestazioni dell'app sono direttamente legate al jank — un ritardo evidente tra l'azione dell'utente e la risposta dell'interfaccia. Cause principali: blocco del thread principale (operazioni pesanti sul thread UI), ridisegni frequenti del layout (overdraw), perdite di memoria (GC frequente), algoritmi non ottimali (O(n²) su grandi dataset). Frame Rate (FPS) — numero di fotogrammi al secondo. Per un'esperienza confortevole sono necessari 60 FPS stabili (Android) o 120 FPS (iPhone Pro, iPad Pro). VSync sincronizza il rendering con la frequenza di aggiornamento dello schermo.

Il jank si verifica quando il rendering di un singolo fotogramma supera 16,6 ms (per 60 FPS) o 8,3 ms (per 120 FPS). Il profiling della GPU (Profile GPU Rendering su Android, Core Animation su iOS) mostra quali fasi di rendering richiedono più tempo. Fasi principali: Layout (posizionamento degli elementi), Draw (disegno), Display (trasferimento al buffer dei fotogrammi). Il problema più comune è l'inflazione del layout in XML, specialmente con ConstraintLayout annidati complessi.

Time-to-Interactive (TTI) — tempo necessario affinché l'app sia completamente pronta per l'interazione. Il TTI include Cold Start, caricamento dati e inizializzazione delle librerie. Google raccomanda TTI inferiore a 5 secondi, Apple — inferiore a 2 secondi per le schermate principali. Lazy Loading — tecnica di caricamento differito di contenuti e librerie, critica per migliorare il TTI. In IT Sectr utilizziamo l'inizializzazione pigra per impostazione predefinita in tutti i progetti.

ANR e Crash

ANR e Crash sono i principali nemici delle prestazioni delle app mobili. ANR (Application Not Responding) — finestra di dialogo su Android che appare se il thread principale è bloccato per più di 5 secondi. Cause: richieste di rete sincrone sul thread UI, lavoro con database senza coroutine, decodifica di bitmap grandi senza downsampling, deadlock sul thread principale. Lo stack di chiamate ANR viene salvato in /data/anr/traces.txt e permette di determinare la posizione esatta del blocco.

Crash — terminazione inaspettata dell'app. Su Android — un'Exception (Java/Kotlin) o Signal (codice nativo). Su iOS — NSException o segnale (EXC_BAD_ACCESS — accesso a memoria liberata). Strumenti di Crash Reporting: Firebase Crashlytics, Sentry, BugSnag. Raccolgono stacktrace, dati del dispositivo e passaggi di riproduzione. Stack Overflow — overflow dello stack di chiamate per ricorsione infinita. OutOfMemoryError — quando l'heap è pieno.

StrictMode — strumento Android per rilevare violazioni della sicurezza dei thread. Permette di impostare regole: ThreadPolicy (vietare disco/rete sul thread principale), VmPolicy (rilevare perdite di Activity, SQLite, CloseGuard). StrictMode dovrebbe essere attivato solo nelle build di debug — nella release non dovrebbe funzionare. Su iOS l'equivalente è Main Thread Checker (Xcode), che rileva automaticamente chiamate UIKit non sul thread principale.

Gestione della memoria (GC, ARC, Retain Cycle)

Perdita di memoria

Una perdita di memoria (Memory Leak) si verifica quando un oggetto rimane in memoria anche se l'app non lo utilizza più. Questo riduce direttamente le prestazioni dell'app. Su Android, il GC (Garbage Collection) non può raccogliere un oggetto se c'è un riferimento forte ad esso. Cause tipiche: riferimenti statici ad Activity, callback/observer non cancellati, classi interne con riferimento implicito alla classe esterna, Handler con messaggi non puliti. LeakCanary — libreria per il rilevamento automatico delle perdite.

Retain Cycle (Ciclo di ritenzione)

ARC (Automatic Reference Counting) — modello di gestione della memoria su iOS. Ogni oggetto ha un contatore di riferimenti (retain count). Quando il contatore arriva a zero, la memoria viene liberata. Un Retain Cycle si verifica quando due oggetti mantengono riferimenti forti l'uno verso l'altro (A → B e B → A). ARC non azzererà mai i contatori. Soluzione: riferimenti deboli (weak) o senza proprietario (unowned). Weak viene automaticamente nullificato (diventa nil) al rilascio dell'oggetto. Unowned non viene nullificato ma garantisce che l'oggetto sia vivo.

GC vs ARC

GC (Garbage Collection) funziona su Android (Java/Kotlin). Il GC sospende periodicamente l'esecuzione (pausa Stop-the-World) per trovare e liberare oggetti irraggiungibili. Attivazione del GC: quando l'heap raggiunge una certa percentuale di riempimento. ARC funziona su iOS (Swift/Objective-C) e non ha pause — i contatori vengono aggiornati atomicamente a ogni assegnazione. ARC è più prevedibile ma può accumulare operazioni retain/release eccessive con alta frequenza di assegnazioni.

Riferimento debole (Weak Reference) e riferimento forte (Strong Reference) — il tipo di riferimento determina se GC/ARC può liberare l'oggetto. Strong Reference — l'oggetto non verrà raccolto finché esiste questo riferimento. Weak Reference — GC/ARC può raccogliere l'oggetto; il riferimento debole diventa nil (in Swift/Java WeakReference). Unowned Reference (Swift) — non viene nullificato al rilascio; accedervi dopo la morte dell'oggetto causa un crash. Su Android si usa java.lang.ref.WeakReference per i riferimenti deboli.

Esempio di rilevamento di una perdita su Android con LeakCanary:

kotlin
// Утечка: анонимный класс держит ссылку на Activity
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        val handler = object : Handler(Looper.getMainLooper()) {
            override fun handleMessage(msg: Message) {
                // Используем `this@MainActivity`, сохраняя ссылку на Activity
                Log.d("TAG", "Handler received message")
            }
        }
        handler.sendEmptyMessageDelayed(0, 60000)
    }
}

// Исправление: статический Handler + WeakReference
class SafeHandler(activity: MainActivity) : Handler() {
    private val weakActivity =
        WeakReference(activity)

    override fun handleMessage(msg: Message) {
        weakActivity.get() ?: return
        Log.d("TAG", "Handler received message")
    }
}

Profiling (Instruments, Android Profiler)

Il profiling è il processo di misurazione delle prestazioni dell'app: CPU, memoria, rete, consumo energetico. Senza profiling, l'ottimizzazione alla cieca è inutile — non saprai quale parte del codice è effettivamente lenta.

Strumento Piattaforma Misura Quando usarlo
Instruments (Time Profiler)iOSCPU, chiamate di funzione, tempo di esecuzioneOttimizzazione algoritmi, ricerca colli di bottiglia
Instruments (Allocations)iOSMemoria, numero di oggetti, retain countsRicerca perdite e consumo eccessivo di memoria
Instruments (Leaks)iOSRetain cycles, perdite di memoriaControllo regolare prima del rilascio
Android Profiler (CPU)AndroidUtilizzo CPU, attività thread, tracesRicerca blocchi del thread principale
Android Profiler (Memory)AndroidHeap dump, tracciamento allocazioniRicerca perdite, analisi oggetti
Android Profiler (Network)AndroidTraffico, velocità, tempi di richiestaOttimizzazione chiamate di rete
LeakCanaryAndroidRilevamento automatico perdite di memoriaIn tutte le fasi di sviluppo
StrictModeAndroidDisco/rete sul thread principale, perditeBuild di debug
Traceview / SystraceAndroidTracciamento metodi, eventi di sistemaAnalisi approfondita della latenza

Instruments (Xcode) — lo strumento più potente per iOS. Time Profiler mostra quali funzioni consumano più CPU. Allocations traccia creazione e rilascio degli oggetti. Leaks trova automaticamente i retain cycles. Fasi del profiling: (1) avviare Instruments; (2) selezionare il modello (Time Profiler per CPU); (3) eseguire lo scenario problematico; (4) analizzare lo stack di chiamate — la colonna più larga è la funzione più "calda".

Android Profiler è integrato in Android Studio (View → Tool Windows → Profiler). CPU Profiler mostra il carico di ogni thread. Memory Profiler — heap dump e tracciamento allocazioni. Network Profiler — tutte le richieste HTTP con tempi. Energy Profiler — consumo energetico: WakeLock, Location, Network. Per tracciamento dettagliato si usa Systrace (Android 10+) o Perfetto — tracciamento di sistema con precisione al microsecondo.

Avvio dell'app (Cold/Warm/Hot Start)

L'avvio dell'app è uno degli indicatori chiave delle prestazioni. Si divide in tre tipi: Cold Start — l'app viene avviata da zero: creazione del processo, Application.onCreate (Android) / AppDelegate.applicationDidFinishLaunching (iOS), caricamento classi, inizializzazione librerie. Warm Start — il processo esiste, ma Activity/ViewController è distrutto (ad esempio, alla rotazione dello schermo o al ritorno dalla memoria). Hot Start — Activity/ViewController è in memoria, l'app viene semplicemente mostrata (passaggio da un'altra app).

Cold Start è la metrica più importante. Su Android include: (1) launch Activity — caricamento XML, inizializzazione View; (2) primo fotogramma — tempo fino al primo rendering. Google raccomanda: launch Activity < 200 ms, primo fotogramma < 500 ms, TTI < 5 secondi. Ottimizzazione del Cold Start: ridurre Application.onCreate (coroutine per inizializzazione pigra), usare SplashScreen API (Android 12+), posticipare l'inizializzazione delle librerie (WorkManager, DI), rimuovere ContentProviders superflui.

Su iOS, il Cold Start include: caricamento del binario Mach-O, dyld (linker dinamico), inizializzazione del runtime Objective-C, application delegate, primo controller. Chrome Custom Tabs (Android) e Universal Links (iOS) — tecnologie per aprire rapidamente contenuti esterni nell'app senza un Cold Start completo. Si consiglia di testare il Cold Start su dispositivi reali di fascia media.

Ottimizzazione delle dimensioni

La dimensione dell'app è un fattore di prestazioni per l'installazione e gli aggiornamenti. Influisce sulla conversione: ogni 10 MB riducono la conversione dell'1%. Google Play raccomanda dimensioni APK inferiori a 150 MB; App Store — inferiori a 200 MB (reti cellulari — 100 MB). Principali metodi di ottimizzazione: compressione delle immagini (WebP invece di PNG risparmia 25-35%), vettorizzazione (VectorDrawable su Android, SF Symbols su iOS), rimozione codice inutilizzato (R8/ProGuard), rimozione risorse inutilizzate (lint → unused resources).

App Bundle (Android) — formato di pubblicazione in cui Google Play genera un APK ottimizzato per ogni dispositivo. App Bundle riduce la dimensione del download del 20-40%. Dynamic Delivery — moduli scaricati su richiesta (on-demand feature modules). Su iOS l'equivalente sono le On-Demand Resources (ODR): risorse scaricate dopo il primo avvio (livelli di gioco, video).

Lazy Loading — tecnica in cui moduli e librerie non vengono caricati all'avvio ma caricati secondo necessità. Split APK (Android) e App Slicing (iOS) — divisione dell'app in slot di architettura: arm64-v8a, x86_64. Ottimizzazione delle dimensioni dell'App — un processo continuo: analizza la composizione dell'APK (Analyze APK in Android Studio), rimuovi icone duplicate, usa SVG invece di più densità PNG. In IT Sectr includiamo il controllo delle dimensioni della build nel CI/CD per ogni MR.

Domande frequenti

Cos'è ANR e come evitarlo?

ANR (Application Not Responding) — finestra di dialogo che appare su Android se il thread principale è bloccato per più di 5 secondi. Per evitare ANR, sposta tutte le operazioni pesanti (rete, database, elaborazione file) in thread in background. L'equivalente su iOS è frozen UI, quando l'app smette di rispondere ai tocchi.

Cos'è una perdita di memoria e un Retain Cycle?

Una perdita di memoria si verifica quando un oggetto non può essere liberato perché esistono ancora riferimenti ad esso. Un Retain Cycle è una situazione in iOS/Objective-C in cui due oggetti si riferiscono l'uno all'altro (A → B → A) e ARC non può liberare nessuno dei due. Soluzione: riferimenti weak/unowned e pulizia tempestiva delle callback.

Quali strumenti usare per il profiling?

Per iOS: Instruments (Time Profiler, Allocations, Leaks). Per Android: Android Profiler (CPU, Memory, Network), LeakCanary (perdite di memoria), StrictMode (violazioni dei thread). Si consiglia di combinare il profiling durante lo sviluppo e l'integrazione.

In cosa differisce Cold Start da Warm Start e Hot Start?

Cold Start — l'app viene avviata da zero: il processo viene creato, le classi vengono caricate, Application.onCreate viene eseguito. Warm Start — il processo esiste ma Activity/ViewController viene ricreata. Hot Start — Activity/ViewController è già in memoria, semplicemente mostrata. Cold Start è il più lento (1-5 secondi) ed è critico per l'esperienza utente.

Come ridurre le dimensioni di un'app mobile?

Metodi principali: rimuovere risorse e codice inutilizzati (usare R8/ProGuard), vettorizzare le immagini (VectorDrawable, SF Symbols), comprimere PNG/WebP (Android), usare App Bundle invece di APK, rimuovere librerie superflue, usare Lazy Loading per i moduli. L'ottimizzazione delle dimensioni può ridurre l'APK del 40-60%.

Riepilogo

  • ANR e Crash — i principali problemi di stabilità; risolti con thread in background e crash reporter
  • Perdita di memoria e Retain Cycle — cause principali di OOM; risolte con riferimenti deboli e LeakCanary
  • GC (pause Stop-the-World) vs ARC (nessuna pausa ma retain cycles) — diversi modelli di memoria
  • Profiling — fase obbligatoria: Instruments (iOS), Android Profiler, LeakCanary, StrictMode
  • Cold Start — metrica chiave; ottimizzare Application.onCreate e inizializzazione pigra
  • App Bundle e WebP/VectorDrawable — strumenti principali per ridurre le dimensioni del 20-60%
  • Le prestazioni sono un processo continuo, non un'attività una tantum; integra le metriche nel CI/CD

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.

Discuti il progetto