0Pricing
Android Academy · Aula

Cache e desempenho

Noções básicas de cache em memória e no disco.

Cache e desempenho é uma aula grátis de Android Academy no CoddyKit. Esta é a aula 3 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de Android Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de Android Academy inclui 4 aulas no total.

Por que o armazenamento em cache é importante

Baixar e decodificar uma imagem é caro: isso usa largura de banda, bateria e CPU. Se você buscar a mesma imagem novamente sempre que uma lista rolar, seu aplicativo parecerá lento e consumirá muitos dados.

O armazenamento em cache guarda imagens decodificadas e bytes baixados, para que a próxima solicitação seja quase instantânea. O Coil vem, por padrão, com dois caches: um cache na memória (rápido, com bitmaps decodificados) e um cache em disco (persistente, com bytes brutos).

Esta lição explica como eles funcionam e como ajustá-los.

Duas camadas: memória e disco

O Coil verifica os caches nesta ordem:

  • Cache de memória: mantém na RAM os bitmaps decodificados usados recentemente. Um acerto aqui é instantâneo, sem necessidade de decodificação.
  • Cache em disco: armazena no dispositivo os bytes originais baixados. Um acerto aqui ignora a rede, mas ainda exige decodificação.
  • Rede: o caminho lento, usado apenas quando não há acerto em nenhum dos dois caches.

A memória é rápida, mas pequena, e é limpa quando o aplicativo é encerrado; o disco é maior e permanece após as reinicializações.

Configurando o ImageLoader

Os caches ficam no ImageLoader, o mecanismo por trás de cada solicitação. Normalmente, você cria um loader personalizado e o reutiliza. Aqui, dimensionamos o cache de memória como uma porcentagem da RAM disponível e o cache em disco com um limite fixo de bytes.

Um loader singleton evita caches duplicados e desperdício de memória.

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()

Fornecendo um loader singleton

O Coil 3 permite definir um único loader para todo o aplicativo implementando SingletonImageLoader.Factory na sua classe Application. Assim, todo AsyncImage o utiliza automaticamente, sem necessidade de passar um loader todas as vezes.

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()
}

Controlando as políticas de cache

Cada solicitação pode ativar ou desativar o armazenamento em cache com memoryCachePolicy e diskCachePolicy. As políticas são ENABLED, READ_ONLY, WRITE_ONLY ou DISABLED.

Por exemplo, desative o cache de memória para imagens enormes e usadas uma única vez que você não queira manter na RAM, ou desative o cache em disco para conteúdo sensível.

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()

Redimensionamento: não decodifique bitmaps enormes

O maior erro de desempenho é decodificar uma foto de 4000x3000 em uma miniatura de 100dp. Isso desperdiça memória e tempo. Por padrão, o Coil dimensiona o bitmap de acordo com os limites medidos do composable, então um AsyncImage pequeno decodifica um bitmap pequeno.

Você pode forçar um tamanho de destino explícito com size() na solicitação quando souber as dimensões.

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()

Chaves de cache estáveis

O Coil deriva uma chave de cache dos dados da solicitação. Se suas URLs contiverem parâmetros de consulta variáveis (como um token assinado), imagens idênticas receberão chaves diferentes e nunca encontrarão um acerto no cache.

Defina explicitamente memoryCacheKey e diskCacheKey com base na parte estável, como o identificador da imagem, para que o armazenamento em cache funcione.

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()

Pré-carregando imagens

Para uma experiência mais fluida, você pode aquecer o cache antes de uma imagem ser exibida, por exemplo, pré-buscando a próxima página de um feed. Use o enqueue do loader com uma solicitação que não tenha destino.

Quando o usuário chegar à imagem, ela já estará armazenada em cache e aparecerá instantaneamente.

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)
    }
}

Limpando o cache

Às vezes, você precisa liberar espaço ou forçar uma atualização, por exemplo, depois que o usuário atualizar seu avatar. Você pode limpar o cache de memória, o cache em disco ou remover uma única chave.

Limpar tudo é uma operação pesada; prefira remover chaves específicas quando possível.

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 versus acertos no cache

O crossfade fica ótimo para carregamentos pela rede, mas pode parecer lento quando uma imagem vem diretamente do cache de memória e poderia aparecer instantaneamente. O Coil é inteligente nesse caso: por padrão, ele ignora a animação de crossfade para acertos no cache de memória e anima apenas imagens realmente carregadas.

Assim, você obtém transições suaves no primeiro carregamento e exibição instantânea ao rolar por imagens armazenadas em cache, sem necessidade de trabalho extra.

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()

Lista de verificação de desempenho para listas

Ao exibir muitas imagens em uma LazyColumn ou grade, siga estas regras para obter uma rolagem suave:

  • Dê a cada imagem um tamanho fixo para que o Coil decodifique um bitmap pequeno.
  • Reutilize um único ImageLoader singleton para compartilhar os caches.
  • Use ContentScale.Crop para evitar mudanças no layout.
  • Mantenha as chaves de cache estáveis entre as recargas.
  • Deixe o Coil cancelar automaticamente as solicitações fora da tela; não tente impedi-lo.
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ção rápida

O Coil verifica seus caches antes de acessar a rede. Qual cache, quando encontra um acerto, é mais rápido porque ignora completamente a decodificação?

Recapitulação

Você aprendeu como o Coil mantém as imagens rápidas:

  • Dois caches: memória (decodificado, rápido e pequeno) e disco (bytes brutos, persistente e maior).
  • Configure os tamanhos em um ImageLoader personalizado e disponibilize um único loader por meio de SingletonImageLoader.Factory.
  • Ajuste o comportamento de cada solicitação com memoryCachePolicy e diskCachePolicy.
  • Decodifique sempre no tamanho de exibição; defina size() explicitamente quando necessário.
  • Use chaves de cache estáveis, pré-carregue as próximas imagens e limpe chaves específicas para atualizar o conteúdo.

A seguir: indo além das imagens para reproduzir áudio e vídeo.

Perguntas Frequentes

A aula “Cache e desempenho” é grátis?

Sim — o texto completo de “Cache e desempenho” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de Android Academy, atualize para CoddyKit PRO. O curso de Android Academy inclui 4 aulas no total.

O que vou aprender em “Cache e desempenho”?

Noções básicas de cache em memória e no disco. Você pratica Android Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar Android Academy?

Nenhuma experiência prévia é necessária. Android Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 3 de 4.

Quanto tempo leva a aula “Cache e desempenho”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de Android Academy?

Sim. Cada aula de Android Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. Carregando imagens com Coil
  2. Marcadores e estados de erro
  3. Cache e desempenho
  4. Reproduzindo áudio e vídeo
← Voltar para Android Academy