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
ImageLoadersingleton para compartilhar os caches. - Use
ContentScale.Croppara 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
ImageLoaderpersonalizado e disponibilize um único loader por meio deSingletonImageLoader.Factory. - Ajuste o comportamento de cada solicitação com
memoryCachePolicyediskCachePolicy. - 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.