Se non fai attenzione, lavorare con le immagini può causare rapidamente problemi di prestazioni. Anche una piccola grafica in un formato compresso come JPG o PNG può trasformarsi in una bitmap di grandi dimensioni quando viene decodificata per la visualizzazione. Se non utilizzi in modo efficiente la grafica, potresti riscontrare problemi di memoria che possono influire sulle prestazioni della tua app e di altre app sul dispositivo. Segui queste best practice per assicurarti che la tua app funzioni al meglio.
Utilizzare le librerie di caricamento delle immagini
Puoi migliorare l'efficienza della tua app utilizzando librerie di caricamento delle immagini come Coil (per i progetti Kotlin-first) o Glide (per i progetti Java). Queste librerie riducono la memoria utilizzata dalla tua app eseguendo operazioni come la memorizzazione nella cache delle immagini, il downsampling della grafica quando necessario e il riciclo degli oggetti grafici.
Ridurre la risoluzione delle immagini
Assicurati di utilizzare le dimensioni dell'immagine appropriate per le tue esigenze. Dovresti evitare di caricare un'immagine grande ad alta risoluzione in un contenitore piccolo (ad esempio una miniatura). Utilizza invece il sottocampionamento per ridurre le dimensioni dell'immagine prima di decodificarla in memoria.
Riduzione del campionamento lato client
Librerie di caricamento delle immagini come Coil e Glide gestiscono automaticamente il downsampling. Puoi configurare le strategie di sottocampionamento
utilizzando ImageLoader (per Coil) o DownsampleStrategy
(per Glide). Se gestisci manualmente le bitmap, puoi utilizzare
inSampleSize per decodificare una versione più piccola. Per farlo in modo sicuro, devi
prima impostare inJustDecodeBounds su true per leggere le dimensioni dell'immagine
senza allocare memoria, calcolare la dimensione del campione, impostare inSampleSize su
questo valore, impostare inJustDecodeBounds su false e poi decodificare l'immagine.
Preferisci il ridimensionamento lato server
Se possibile, richiedi le dimensioni esatte dell'immagine di cui hai bisogno direttamente dal server di backend. In questo modo si riduce l'utilizzo della rete e l'ingombro della cache del disco, mentre si ottiene una memoria utilizzata più leggera evitando l'overhead di memoria del ridimensionamento delle immagini sul dispositivo.
Puoi configurare le librerie in modo da aggiungere dinamicamente le dimensioni della visualizzazione di destinazione all'URL dell'immagine. Ad esempio, Coil lo consente utilizzando intercettori personalizzati e Glide lo supporta utilizzando caricatori di modelli personalizzati (come BaseGlideUrlLoader).
Evita dimensioni del layout senza vincoli
Affinché i caricatori di immagini eseguano il sottocampionamento (lato client o lato server) in modo efficace, devono conoscere le dimensioni target prima di eseguire la richiesta.
Evita di utilizzare wrapContentSize o di lasciare le dimensioni senza vincoli nei
composables che caricano immagini remote. Se queste librerie non riescono a dedurre i limiti di destinazione, caricano l'immagine originale a grandezza naturale.
Ciò può comportare il caricamento di un'immagine molto più grande del necessario, aumentando
l'utilizzo della memoria e la latenza.
Imposta invece dimensioni esplicite nel composable dell'immagine (ad esempio utilizzando
Modifier.size) o definisci le proporzioni. In questo modo, il motore di layout può calcolare in anticipo il target di pixel esatto, che il caricatore di immagini può quindi utilizzare per richiedere e decodificare l'asset delle dimensioni corrette.
Fornire risorse alternative per schermi di dimensioni diverse
Se spedisci immagini con la tua app, valuta la possibilità di fornire asset di dimensioni diverse per le diverse risoluzioni dei dispositivi. Ciò può contribuire a ridurre le dimensioni del download della tua app sui dispositivi e a migliorare le prestazioni, in quanto caricherà un'immagine a risoluzione inferiore su un dispositivo a risoluzione inferiore. Per saperne di più su come fornire bitmap alternativi per diverse dimensioni dei dispositivi, consulta la documentazione sui bitmap alternativi.
Non applicare direttamente il padding
A volte potrebbe essere necessario aggiungere un riempimento a un'immagine. Ad esempio, potresti
voler circondare l'immagine con un bordo trasparente per il letterbox.
In questi casi, non aggiungere il padding direttamente all'immagine, modificandone
le dimensioni. Lascia invece le dimensioni dell'immagine così come sono
e regola la posizione dell'immagine sullo schermo utilizzando InsetDrawable.
In alternativa, puoi aggiungere un riempimento al composable o alla visualizzazione che contiene l'immagine.
Scegliere il formato pixel giusto
Trova il giusto equilibrio tra memoria e qualità scegliendo il formato pixel corretto. Utilizza RGB_565
quando non hai bisogno di trasparenza; questo formato utilizza la metà della memoria del formato
ARGB_8888 predefinito.
In Glide puoi configurare questa opzione utilizzando DecodeFormat. In Coil, puoi
utilizzare la proprietà bitmapConfig.
Utilizza i vettori, se possibile
Per le immagini composte da forme geometriche, una grafica vettoriale è molto più piccola di una
bitmap e si adatta perfettamente a qualsiasi densità di visualizzazione. Se opportuno, utilizza elementi come ShapeDrawable per rappresentare la grafica.
Rilascia e riutilizza le bitmap quando possibile
I file grafici di grandi dimensioni possono occupare molta memoria. Per ridurre il loro impatto, devi rilasciare o riutilizzare gli oggetti grafici ogni volta che puoi.
Se utilizzi una libreria di caricamento delle immagini, assicurati di rilasciare le bitmap nel pool gestito della libreria quando non ti servono più. La libreria può riutilizzare gli oggetti quando necessario e mantiene un buffer di memoria disponibile per esigenze future.
Se gestisci manualmente la grafica, devi rilasciare
le bitmap quando hai finito di utilizzarle chiamando Bitmap.recycle
e scartando immediatamente il riferimento Bitmap, anziché
affidarti alla garbage collection.
Altri suggerimenti utili
Questa sezione elenca alcuni altri modi per migliorare le prestazioni dell'app durante la gestione della grafica.
Non includere immagini di grandi dimensioni nel file AAB/APK
Una delle principali cause delle grandi dimensioni del download delle app sono le grafiche incluse nel file AAB o APK. Utilizza lo strumento APK Analyzer per assicurarti di non includere file di immagine più grandi del necessario. Riduci le dimensioni o valuta la possibilità di inserire le immagini su un server e scaricarle solo quando necessario.
Trovare bitmap ridondanti
Se hai diverse copie della stessa immagine, sprechi memoria. Puoi utilizzare il profiler di Android Studio per identificare le risorse grafiche ridondanti. Utilizza l'analizzatore dump dell'heap per acquisire un dump dell'heap e filtra i risultati scegliendo l'impostazione bitmap duplicati.
Quando utilizzi ImageBitmap, chiama prepareToDraw prima di disegnare
Quando utilizzi ImageBitmap, per avviare il processo di caricamento della texture sulla GPU, chiama ImageBitmap#prepareToDraw() prima di disegnarla effettivamente. In questo modo, la GPU prepara la texture e migliora il rendimento della visualizzazione di un elemento visivo sullo schermo. La maggior parte delle librerie di caricamento delle immagini esegue già questa ottimizzazione, ma
se lavori direttamente con la classe ImageBitmap, è qualcosa da
tenere a mente.
Preferisci passare un Int DrawableRes o un URL come parametri nel tuo elemento componibile anziché Painter
A causa della complessità della gestione delle immagini (ad esempio, scrivere una funzione uguale per Bitmaps sarebbe costoso dal punto di vista computazionale), l'API Painter non è esplicitamente contrassegnata come stabile con l'annotazione @Stable. Le classi instabili possono portare a ricomposizioni non necessarie perché il
compilatore non può dedurre facilmente se i dati sono cambiati.
Pertanto, ti consigliamo di passare un URL o un ID risorsa disegnabile come parametri
al tuo componibile, anziché passare un Painter come parametro.
// Prefer this:
@Composable
fun MyImage(url: String) {
}
// Over this:
@Composable
fun MyImage(painter: Painter) {
}
Consigliati per te
- Nota: il testo del link viene visualizzato quando JavaScript è disattivato
- ImageBitmap e ImageVector {:#bitmap-vs-vector}
- Salvare lo stato dell'interfaccia utente in Compose
- Fasi di Jetpack Compose