0Pricing
Android Academy · Lezione

Caching e prestazioni

Nozioni di base sulla cache in memoria e su disco

Caching e prestazioni è una lezione Android Academy gratuita su CoddyKit. Questa è la lezione 3 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento Android Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso Android Academy include 4 lezioni in totale.

Perché il caching è importante

Scaricare e decodificare un'immagine è costoso: utilizza banda, batteria e CPU. Se scarica di nuovo la stessa immagine ogni volta che si scorre un elenco, l'app risulta lenta e consuma dati inutilmente.

Il caching memorizza le immagini decodificate e i byte scaricati, così la richiesta successiva è quasi istantanea. Coil include per impostazione predefinita due cache: una cache in memoria (veloce, con bitmap decodificate) e una cache su disco (persistente, con byte grezzi).

Questa lezione spiega come funzionano e come ottimizzarle.

Due livelli: memoria e disco

Coil controlla le cache in questo ordine:

  • Cache in memoria: contiene in RAM le bitmap decodificate utilizzate di recente. Un riscontro positivo qui è istantaneo e non richiede alcuna decodifica.
  • Cache su disco: memorizza sul dispositivo i byte originali scaricati. Un riscontro positivo qui evita la rete, ma richiede comunque la decodifica.
  • Rete: il percorso più lento, utilizzato solo quando entrambe le cache non contengono il dato.

La memoria è veloce ma limitata e viene svuotata quando l'app termina; il disco è più capiente e sopravvive ai riavvii.

Configurazione di ImageLoader

Le cache risiedono in ImageLoader, il motore alla base di ogni richiesta. Di solito si crea un loader personalizzato e lo si riutilizza. Qui dimensioniamo la cache in memoria in base a una percentuale della RAM disponibile e la cache su disco in base a un limite fisso in byte.

Un loader singleton evita cache duplicate e sprechi di memoria.

import android.content.Context
import coil3.ImageLoader
import coil3.disk.DiskCache
import coil3.memory.MemoryCache
import okio.Path.Companion.toOkioPath

fun newImageLoader(context: Context): ImageLoader =
    ImageLoader.Builder(context)
        .memoryCache {
            MemoryCache.Builder()
                .maxSizePercent(context, 0.25) // 25% of app RAM
                .build()
        }
        .diskCache {
            DiskCache.Builder()
                .directory(context.cacheDir.resolve("image_cache").toOkioPath())
                .maxSizeBytes(50L * 1024 * 1024) // 50 MB
                .build()
        }
        .build()

Fornire un loader singleton

Coil 3 consente di impostare un loader unico a livello dell'applicazione implementando SingletonImageLoader.Factory nella classe Application. In questo modo ogni AsyncImage lo utilizza automaticamente, senza dover passare un loader ogni volta.

import android.app.Application
import coil3.ImageLoader
import coil3.PlatformContext
import coil3.SingletonImageLoader

class MyApp : Application(), SingletonImageLoader.Factory {
    override fun newImageLoader(context: PlatformContext): ImageLoader =
        ImageLoader.Builder(context)
            .crossfade(true)
            .build()
}

Controllare le politiche della cache

Ogni richiesta può attivare o disattivare il caching con memoryCachePolicy e diskCachePolicy. Le politiche sono ENABLED, READ_ONLY, WRITE_ONLY o DISABLED.

Ad esempio, disabiliti la cache in memoria per immagini enormi e occasionali che non vuole conservare nella RAM, oppure disabiliti la cache su disco per contenuti sensibili.

import android.content.Context
import coil3.request.CachePolicy
import coil3.request.ImageRequest

fun noMemoryCacheRequest(context: Context, url: String) =
    ImageRequest.Builder(context)
        .data(url)
        .memoryCachePolicy(CachePolicy.DISABLED)
        .diskCachePolicy(CachePolicy.ENABLED)
        .build()

Ridimensionamento: non decodifichi bitmap enormi

Il più grande errore in termini di prestazioni consiste nel decodificare una foto da 4000x3000 in una miniatura da 100dp. Questo spreca memoria e tempo. Per impostazione predefinita, Coil dimensiona la bitmap in base ai limiti misurati del composable, quindi un AsyncImage piccolo decodifica una bitmap piccola.

Quando conosce le dimensioni, può forzare una dimensione di destinazione esplicita con size() nella richiesta.

import android.content.Context
import coil3.request.ImageRequest
import coil3.size.Size

fun thumbnailRequest(context: Context, url: String) =
    ImageRequest.Builder(context)
        .data(url)
        .size(Size(200, 200)) // decode at most 200x200
        .build()

Chiavi di cache stabili

Coil ricava una chiave di cache dai dati della richiesta. Se gli URL contengono parametri di query variabili (come un token firmato), immagini identiche ricevono chiavi diverse e non trovano mai corrispondenza nella cache.

Imposti memoryCacheKey e diskCacheKey esplicite basate sulla parte stabile, ad esempio l'ID dell'immagine, affinché il caching funzioni.

import android.content.Context
import coil3.request.ImageRequest

fun stableKeyRequest(context: Context, id: String, signedUrl: String) =
    ImageRequest.Builder(context)
        .data(signedUrl)
        .memoryCacheKey("image_$id")
        .diskCacheKey("image_$id")
        .build()

Precaricamento delle immagini

Per un'esperienza più fluida può riscaldare la cache prima che un'immagine venga mostrata, ad esempio precaricando la pagina successiva di un feed. Utilizzi enqueue del loader con una richiesta priva di target.

Quando l'utente raggiunge l'immagine, questa è già nella cache e appare istantaneamente.

import android.content.Context
import coil3.ImageLoader
import coil3.request.ImageRequest

fun preload(context: Context, loader: ImageLoader, urls: List<String>) {
    urls.forEach { url ->
        val request = ImageRequest.Builder(context)
            .data(url)
            .build()
        loader.enqueue(request)
    }
}

Svuotare la cache

Talvolta è necessario liberare spazio o forzare un aggiornamento, ad esempio dopo che l'utente ha aggiornato il proprio avatar. Può svuotare la cache in memoria, la cache su disco oppure rimuovere una singola chiave.

Svuotare tutto è un'operazione pesante: quando possibile, preferisca rimuovere chiavi specifiche.

import coil3.ImageLoader
import coil3.memory.MemoryCache

fun refreshAvatar(loader: ImageLoader, key: String) {
    // Remove one entry so the next load refetches
    loader.memoryCache?.remove(MemoryCache.Key(key))
    loader.diskCache?.remove(key)
}

fun clearAll(loader: ImageLoader) {
    loader.memoryCache?.clear()
    // Disk clearing is heavier; do it sparingly
}

Crossfade e riscontri nella cache

Il crossfade è ottimo per i caricamenti dalla rete, ma può sembrare lento quando un'immagine proviene direttamente dalla cache in memoria e potrebbe apparire istantaneamente. Coil gestisce intelligentemente questo caso: per impostazione predefinita salta l'animazione crossfade per i riscontri nella cache in memoria e anima solo le immagini caricate effettivamente.

In questo modo ottiene dissolvenze fluide al primo caricamento e visualizzazione istantanea durante lo scorrimento di immagini nella cache, senza dover fare altro.

import android.content.Context
import coil3.request.ImageRequest
import coil3.request.crossfade

fun feedRequest(context: Context, url: String) =
    ImageRequest.Builder(context)
        .data(url)
        // Crossfade plays on real loads; memory-cache hits show instantly
        .crossfade(true)
        .build()

Checklist delle prestazioni per gli elenchi

Quando mostra molte immagini in una LazyColumn o in una griglia, segua queste regole per uno scorrimento fluido:

  • Assegni a ogni immagine una dimensione fissa, così Coil decodifica una bitmap piccola.
  • Riutilizzi un unico ImageLoader singleton, così le cache vengono condivise.
  • Utilizzi ContentScale.Crop per evitare spostamenti del layout.
  • Mantenga stabili le chiavi di cache tra un ricaricamento e l'altro.
  • Lasci che Coil annulli automaticamente le richieste fuori schermo: non interferisca.
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import coil3.compose.AsyncImage

@Composable
fun PhotoGrid(urls: List<String>) {
    LazyVerticalGrid(columns = GridCells.Fixed(3)) {
        items(urls) { url ->
            AsyncImage(
                model = url,
                contentDescription = null,
                contentScale = ContentScale.Crop,
                modifier = Modifier.aspectRatio(1f)
            )
        }
    }
}

Verifica rapida

Coil controlla le proprie cache prima di accedere alla rete. Quale cache, quando viene trovata, è la più veloce perché evita completamente la decodifica?

Riepilogo

Ha imparato come Coil mantiene veloci le immagini:

  • Due cache: memoria (decodificata, veloce, limitata) e disco (byte grezzi, persistente, più capiente).
  • Configuri le dimensioni in un ImageLoader personalizzato e ne renda disponibile uno tramite SingletonImageLoader.Factory.
  • Ottimizzi il comportamento per ogni richiesta con memoryCachePolicy e diskCachePolicy.
  • Decodifichi sempre alla dimensione di visualizzazione; imposti size() in modo esplicito quando necessario.
  • Utilizzi chiavi di cache stabili, precarichi le immagini successive e cancelli chiavi specifiche per forzare un aggiornamento.

Prossimo argomento: andare oltre le immagini per riprodurre audio e video.

Domande Frequenti

La lezione «Caching e prestazioni» è gratuita?

Sì — il testo completo di «Caching e prestazioni» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso Android Academy, passa a CoddyKit PRO. Il corso Android Academy include 4 lezioni in totale.

Cosa imparerò in «Caching e prestazioni»?

Nozioni di base sulla cache in memoria e su disco Eserciti Android Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare Android Academy?

Non è richiesta alcuna esperienza precedente. Android Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 3 di 4.

Quanto tempo richiede la lezione «Caching e prestazioni»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione Android Academy?

Sì. Ogni lezione Android Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. Caricare immagini con Coil
  2. Placeholder e stati di errore
  3. Caching e prestazioni
  4. Riprodurre audio e video
← Torna a Android Academy