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) {
}
Recomendados para você
- Observação: o texto do link aparece quando o JavaScript está desativado.
- ImageBitmap x ImageVector {:#bitmap-vs-vector}
- Salvar o estado da interface no Compose
- Fases do Jetpack Compose