Otimizar imagens bitmap

Trabalhar com imagens pode introduzir problemas de performance rapidamente se você não tomar cuidado. Até mesmo um pequeno gráfico em um formato compactado, como JPG ou PNG, pode se transformar em um bitmap grande quando é decodificado para exibição. Se você não for eficiente no uso de gráficos, poderá ter problemas de memória que podem prejudicar o desempenho do seu app e de outros apps no dispositivo. Siga estas práticas recomendadas para garantir que o app tenha a melhor performance.

Usar bibliotecas de carregamento de imagens

Você pode melhorar a eficiência do app usando bibliotecas de carregamento de imagens, como Coil (para projetos que priorizam o Kotlin) ou Glide (para projetos Java). Essas bibliotecas reduzem o uso da memória do app ao realizar ações como armazenar imagens em cache, reduzir a resolução de gráficos quando necessário e reciclar objetos gráficos.

Reduzir a resolução de imagens

Use o tamanho de imagem adequado para suas necessidades. Evite carregar uma imagem grande e de alta resolução em um contêiner pequeno, como uma miniatura. Em vez disso, use a redução de resolução para diminuir a imagem antes de decodificá-la na memória.

Downsampling do lado do cliente

Bibliotecas de carregamento de imagens, como Coil e Glide, processam a redução de resolução automaticamente. É possível configurar as estratégias de redução de resolução usando ImageLoader (para Coil) ou DownsampleStrategy (para Glide). Se você estiver gerenciando bitmaps manualmente, use inSampleSize para decodificar uma versão menor. Para fazer isso com segurança, primeiro defina inJustDecodeBounds como true para ler as dimensões da imagem sem alocar memória, calcule o tamanho da amostra, defina inSampleSize como esse valor, defina inJustDecodeBounds como false e decodifique a imagem.

Preferir o redimensionamento do lado do servidor

Sempre que possível, solicite as dimensões exatas da imagem que você precisa diretamente do seu servidor de back-end. Isso reduz o uso da rede e o consumo do cache em disco, além de diminuir o uso da memória, evitando a sobrecarga de memória do redimensionamento de imagens no dispositivo.

É possível configurar bibliotecas para anexar dinamicamente o tamanho da visualização de destino ao URL da imagem. Por exemplo, o Coil permite isso usando interceptadores personalizados, e o Glide oferece suporte usando carregadores de modelo personalizados (como BaseGlideUrlLoader).

Evitar tamanhos de layout sem restrições

Para que os carregadores de imagens façam a redução de resolução (no lado do cliente ou do servidor) de forma eficaz, eles precisam saber o tamanho de destino antes de executar a solicitação.

Evite usar wrapContentSize ou deixar dimensões sem restrições em elementos de composição que carregam imagens remotas. Se essas bibliotecas não puderem inferir os limites de destino, elas vão carregar a imagem original em tamanho completo. Isso pode levar ao carregamento de uma imagem substancialmente maior do que o necessário, aumentando o uso da memória e a latência.

Em vez disso, defina dimensões explícitas no combinável de imagem (por exemplo, usando Modifier.size) ou defina uma proporção. Isso permite que o mecanismo de layout calcule o destino exato de pixels com antecedência, que o carregador de imagens pode usar para solicitar e decodificar o recurso de tamanho correto.

Forneça recursos alternativos para diferentes tamanhos de tela

Se você estiver enviando imagens com seu app, forneça recursos de tamanhos diferentes para resoluções de dispositivo distintas. Isso pode ajudar a reduzir o tamanho do download do app em dispositivos e melhorar a performance, já que uma imagem de resolução menor vai ser carregada em um dispositivo de resolução mais baixa. Para saber mais sobre como fornecer bitmaps alternativos para diferentes tamanhos de dispositivo, consulte a documentação alternativa de bitmap.

Não aplique padding diretamente

Às vezes, é necessário adicionar padding a uma imagem. Por exemplo, talvez você queira que a imagem seja cercada por uma borda transparente para letterboxing. Nessas situações, não adicione o padding diretamente à imagem, mudando as dimensões dela. Em vez disso, deixe as dimensões da imagem como estão e ajuste a localização dela na tela usando InsetDrawable. Como alternativa, você pode adicionar padding ao elemento combinável ou à visualização que contém a imagem.

Escolher o formato de pixel certo

Equilibre a memória e a qualidade escolhendo o formato de pixel certo. Use RGB_565 quando não precisar de transparência. Esse formato usa metade da memória do formato padrão ARGB_8888.

No Glide, é possível configurar isso usando DecodeFormat. No Coil, use a propriedade bitmapConfig.

Use vetores sempre que possível

Para imagens compostas por formas geométricas, um gráfico vetorial é muito menor do que um bitmap e é dimensionado sem problemas para qualquer densidade de tela. Quando adequado, use elementos como ShapeDrawable para representar gráficos.

Liberar e reutilizar bitmaps sempre que possível

Arquivos gráficos grandes podem ocupar muita memória. Para reduzir o impacto, libere ou reutilize os objetos gráficos sempre que possível.

Se você usar uma biblioteca de carregamento de imagens, libere bitmaps para o pool gerenciado da biblioteca quando não precisar mais deles. A biblioteca pode reutilizar os objetos quando necessário e mantém um buffer de memória disponível para necessidades futuras.

Se você estiver gerenciando gráficos manualmente, libere bitmaps quando terminar de usá-los chamando Bitmap.recycle e descartando imediatamente a referência Bitmap, em vez de depender da coleta de lixo.

Outras dicas e sugestões

Esta seção lista algumas outras maneiras de melhorar o desempenho do app ao processar gráficos.

Não empacote imagens grandes com o arquivo AAB/APK

O tamanho grande de download de apps se deve principalmente aos gráficos empacotados dentro do arquivo AAB ou APK. Use a ferramenta APK Analyzer para garantir que você não esteja empacotando arquivos de imagem maiores do que o necessário. Reduza os tamanhos ou considere colocar as imagens em um servidor e só fazer o download delas quando necessário.

Encontrar bitmaps redundantes

Se você tiver várias cópias da mesma imagem, isso vai desperdiçar memória. Você pode usar o criador de perfil do Android Studio para identificar gráficos redundantes. Use o analisador de heap dump para capturar um heap dump e filtre os resultados escolhendo a configuração duplicate bitmaps.

Ao usar ImageBitmap, chame prepareToDraw antes da renderização

Ao usar ImageBitmap, para iniciar o processo de upload da textura para a GPU, chame ImageBitmap#prepareToDraw() antes da renderização real. Isso ajuda a GPU a preparar a textura e melhorar a performance da exibição de uma imagem na tela. A maioria das bibliotecas de carregamento de imagem já faz essa otimização, mas se você estiver trabalhando com a classe ImageBitmap por conta própria, é importante se lembrar disso.

Prefira transmitir um Int DrawableRes ou URL como parâmetros para o elemento combinável em vez de Painter

Devido às complexidades de lidar com imagens, por exemplo, codificar uma função igual a Bitmaps seria uma opção de computação muito cara, a API Painter não está explicitamente marcada como estável com a anotação @Stable. As classes instáveis podem levar a recomposições desnecessárias porque o compilador não pode inferir com facilidade se os dados mudam.

Por isso, recomendamos transmitir um URL ou um ID de recurso drawable como parâmetro aos elementos de composição, em vez de transmitir um Painter como parâmetro.

// Prefer this:
@Composable
fun MyImage(url: String) {

}
// Over this:
@Composable
fun MyImage(painter: Painter) {

}