Otimização de Memória no Carregamento de Imagens no Android com BitmapFactory

A classe BitmapFactory no ecossistema Android disponibiliza quatro abordagans principais para a decodificação de imagens em objetos Bitmap:

  • decodeFile: Realiza a leitura direta a partir do sistema de arquivos.
  • decodeResource: Extrai imagens dos assets ou pastas de recursos do aplicativo.
  • decodeByteArray: Processa imagens a partir de arrays de bytes.
  • decodeStream: Lê dados através de um fluxo de entrada (InputStream). Esta é a implementação subjacente utilizada por decodeFile e decodeResource.

Carregar imagens de alta resolução dirteamente na memória frequentemente resulta em exceções de estouro de memória (OOM). Para mitigar esse problema, utiliza-se a técnica de subamostragem (downsampling) através do parâmetro inSampleSize presente na classe BitmapFactory.Options.

O fluxo de trabalho para um carregamento otimizado envolve três etapas fundamentais:

  1. Configurar a propriedade inJustDecodeBounds como true. Isso instrui o decodificador a extrair apenas os metadados da imagem (dimensões originais) sem alocar a matriz de pixels na memória.
  2. Obter as dimensões brutas (outWidth e outHeight) e compará-las com as dimensões exigidas pela interface do usuário para calcular a taxa de amostragem ideal.
  3. Redefinir inJustDecodeBounds para false, aplicar o fator de escala calculado e executar a decdoificação final.

Abaixo está a implementação refatorada para realizar esse processo:

public static Bitmap loadScaledBitmap(Resources resources, int imageResId, int targetW, int targetH) {
    BitmapFactory.Options decodingOptions = new BitmapFactory.Options();
    
    // Primeira passagem: extração de metadados sem alocação de memória
    decodingOptions.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(resources, imageResId, decodingOptions);
    
    // Cálculo da taxa de redução baseado nas dimensões originais e alvo
    int scaleFactor = computeSamplingRate(
        decodingOptions.outWidth, 
        decodingOptions.outHeight, 
        targetW, 
        targetH
    );
    
    // Segunda passagem: decodificação real aplicando a subamostragem
    decodingOptions.inSampleSize = scaleFactor;
    decodingOptions.inJustDecodeBounds = false;
    
    return BitmapFactory.decodeResource(resources, imageResId, decodingOptions);
}

A função auxiliar para o cálculo matemático do fator de escala foi isolada para receber as dimensões diretamente, operando da seguinte maneira:

private static int computeSamplingRate(int rawWidth, int rawHeight, int reqWidth, int reqHeight) {
    // Prevenção contra argumentos inválidos
    if (reqWidth <= 0 || reqHeight <= 0) {
        return 1;
    }

    int sampleRate = 1;

    // A subamostragem só é necessária se a imagem original for maior que o alvo
    if (rawHeight > reqHeight || rawWidth > reqWidth) {
        int tempWidth = rawWidth;
        int tempHeight = rawHeight;

        // Determina a maior potência de 2 que mantém a imagem maior ou igual ao alvo
        while ((tempWidth / 2) >= reqWidth && (tempHeight / 2) >= reqHeight) {
            sampleRate *= 2;
            tempWidth /= 2;
            tempHeight /= 2;
        }
    }
    
    return sampleRate;
}

Tags: android BitmapFactory java Otimização de Memória OutOfMemoryError

Publicado em 8-5 16:10