Otimização de bitmap para ajuste de desempenho do Android

fundo

No desenvolvimento do Android, carregar muitas imagens ou imagens muito grandes pode facilmente causar uma exceção OutOfMemoryError, que é o nosso estouro de memória comum. Como o Android impõe um limite de memória em um único aplicativo, a memória alocada padrão é de apenas alguns M (dependendo dos diferentes sistemas). Se a imagem carregada estiver em um formato de compactação como JPG (JPG suporta o nível mais alto de compactação, mas a compactação é com perdas), expandi-la na memória ocupará muito espaço na memória e é fácil causar estouro de memória. Portanto, é muito importante carregar o Bitmap com eficiência. Bitmap refere-se a uma imagem no Android, e o formato da imagem é .jpg .png .webp e outros formatos comuns.

Como escolher um formato de imagem

Um princípio: sob a premissa de garantir que a imagem não seja distorcida visualmente, reduza o tamanho o máximo possível

Os formatos de imagem mais usados ​​atualmente no Android são png, jpeg e webp

  • png: formato de imagem compactada sem perdas, suporta canal alfa, material de imagem de corte do Android adota principalmente este formato
  • jpeg: formato de imagem compactada com perdas, não suporta fundo transparente, adequado para compactação de imagem grande com cores ricas, como fotos, não adequado para logotipo
  • webp: é um formato de imagem que fornece compactação com perdas e compactação sem perdas ao mesmo tempo. É derivado do formato de codificação de vídeo VP8. De acordo com o site oficial do Google, o webp sem perdas é 26% menor que o png em média, e o webp com perdas é 25% menor que jpeg em média. ~34%, webp sem perdas suporta canal alfa, webp com perdas também suporta sob certas condições, webp com perdas suporta após Android4.0 (API 14), suporta sem perdas e transparente após Android4.3 (API18)

O uso do webp pode efetivamente reduzir o espaço em disco ocupado pela imagem, mantendo a clareza da imagem

Compressão de imagem

A compressão da imagem pode ser considerada sob três aspectos:

  1. qualidade

    A compactação de qualidade não alterará o tamanho da imagem na memória, apenas reduzirá o tamanho do espaço em disco ocupado pela imagem, porque a compactação de qualidade não alterará a resolução da imagem e o tamanho da imagem na memória é baseado em um pixel de largura e altura Além disso, como o png é compactação sem perdas, a configuração da qualidade é inválida,

  /**
 * 质量压缩
 *
 * @param format  图片格式 jpeg,png,webp
 * @param quality 图片的质量,0-100,数值越小质量越差
 */
public static void compress(Bitmap.CompressFormat format, int quality) {
    
    
    File sdFile = Environment.getExternalStorageDirectory();
    File originFile = new File(sdFile, "originImg.jpg");
    Bitmap originBitmap = BitmapFactory.decodeFile(originFile.getAbsolutePath());
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    originBitmap.compress(format, quality, bos);
    try {
    
    
        FileOutputStream fos = new FileOutputStream(new File(sdFile, "resultImg.jpg"));
        fos.write(bos.toByteArray());
        fos.flush();
        fos.close();
    } catch (FileNotFoundException e) {
    
    
        e.printStackTrace();
    } catch (IOException e) {
    
    
        e.printStackTrace();
    }
}
  1. Taxa de amostragem

    A compressão da taxa de amostragem é para reduzir a resolução da imagem definindo BitmapFactory.Options.inSampleSize, reduzindo assim o espaço em disco e o tamanho da memória ocupado pela imagem.

    O conjunto inSampleSize fará com que a largura e a altura da imagem compactada sejam 1/inSampleSize, e o tamanho geral se tornará um quarto do inSampleSize da imagem original. Claro, há alguns pontos a serem observados:

    • inSampleSize menor ou igual a 1 será processado como 1

    • inSampleSize só pode ser definido como o quadrado de 2. Se não for o quadrado de 2, ele será eventualmente reduzido ao quadrado mais próximo de 2. Por exemplo, se for definido como 7, será compactado por 4 e se for definido como 15, será compactado por 8.

/**
 * 
 * @param inSampleSize  可以根据需求计算出合理的inSampleSize
 */
public static void compress(int inSampleSize) {
    
    
    File sdFile = Environment.getExternalStorageDirectory();
    File originFile = new File(sdFile, "originImg.jpg");
    BitmapFactory.Options options = new BitmapFactory.Options();
    //设置此参数是仅仅读取图片的宽高到options中,不会将整张图片读到内存中,防止oom
    options.inJustDecodeBounds = true;
    Bitmap emptyBitmap = BitmapFactory.decodeFile(originFile.getAbsolutePath(), options);

    options.inJustDecodeBounds = false;
    options.inSampleSize = inSampleSize;
    Bitmap resultBitmap = BitmapFactory.decodeFile(originFile.getAbsolutePath(), options);
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    resultBitmap.compress(Bitmap.CompressFormat.JPEG, 100, bos);
    try {
    
    
        FileOutputStream fos = new FileOutputStream(new File(sdFile, "resultImg.jpg"));
        fos.write(bos.toByteArray());
        fos.flush();
        fos.close();
    } catch (FileNotFoundException e) {
    
    
        e.printStackTrace();
    } catch (IOException e) {
    
    
        e.printStackTrace();
    }
}
  1. ampliação

    Reduza o tamanho do espaço em disco e o tamanho da memória da imagem reduzindo os pixels da imagem, que podem ser usados ​​para armazenar miniaturas em cache

 /**
     *  缩放bitmap
     * @param context
     * @param id
     * @param maxW
     * @param maxH
     * @return
     */
    public static Bitmap resizeBitmap(Context context,int id,int maxW,int maxH,boolean hasAlpha,Bitmap reusable){
    
    
        Resources resources = context.getResources();
        BitmapFactory.Options options = new BitmapFactory.Options();
        // 只解码出 outxxx参数 比如 宽、高
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(resources,id,options);
        //根据宽、高进行缩放
        int w = options.outWidth;
        int h = options.outHeight;
        //设置缩放系数
        options.inSampleSize = calcuteInSampleSize(w,h,maxW,maxH);
        if (!hasAlpha){
    
    
            options.inPreferredConfig = Bitmap.Config.RGB_565;
        }
        options.inJustDecodeBounds = false;
        //设置成能复用
        options.inMutable=true;
        options.inBitmap=reusable;
        return BitmapFactory.decodeResource(resources,id,options);
    }

    /**
     * 计算缩放系数
     * @param w
     * @param h
     * @param maxW
     * @param maxH
     * @return 缩放的系数
     */
    private static int calcuteInSampleSize(int w,int h,int maxW,int maxH) {
    
    
        int inSampleSize = 1;
        if (w > maxW && h > maxH){
    
    
            inSampleSize = 2;
            //循环 使宽、高小于 最大的宽、高
            while (w /inSampleSize > maxW && h / inSampleSize > maxH){
    
    
                inSampleSize *= 2;
            }
        }
        return inSampleSize;
    }
}

Use a biblioteca JPEG para compactar imagens usando o algoritmo Huffman na camada jni

O mecanismo de imagem do Android usa uma versão castrada do mecanismo skia, que remove o algoritmo Huffman na compactação de imagem

void write_JPEG_file(uint8_t *data, int w, int h, jint q, const char *path) {
    
    
//    3.1、创建jpeg压缩对象
    jpeg_compress_struct jcs;
    //错误回调
    jpeg_error_mgr error;
    jcs.err = jpeg_std_error(&error);
    //创建压缩对象
    jpeg_create_compress(&jcs);
//    3.2、指定存储文件  write binary
    FILE *f = fopen(path, "wb");
    jpeg_stdio_dest(&jcs, f);
//    3.3、设置压缩参数
    jcs.image_width = w;
    jcs.image_height = h;
    //bgr
    jcs.input_components = 3;
    jcs.in_color_space = JCS_RGB;
    jpeg_set_defaults(&jcs);
    //开启哈夫曼功能
    jcs.optimize_coding = true;
    jpeg_set_quality(&jcs, q, 1);
//    3.4、开始压缩
    jpeg_start_compress(&jcs, 1);
//    3.5、循环写入每一行数据
    int row_stride = w * 3;//一行的字节数
    JSAMPROW row[1];
    while (jcs.next_scanline < jcs.image_height) {
    
    
        //取一行数据
        uint8_t *pixels = data + jcs.next_scanline * row_stride;
        row[0]=pixels;
        jpeg_write_scanlines(&jcs,row,1);
    }
//    3.6、压缩完成
    jpeg_finish_compress(&jcs);
//    3.7、释放jpeg对象
    fclose(f);
    jpeg_destroy_compress(&jcs);
}

Por envolver a parte jni, postarei apenas o código usado por enquanto e escreverei alguns blogs sobre a parte jni para compartilhar com vocês mais tarde.

As imagens de configuração podem ser reutilizadas

A multiplexação de imagem refere-se principalmente à multiplexação de blocos de memória, não sendo necessário solicitar uma nova memória para este bitmap, o que evita uma alocação e recuperação de memória, melhorando assim a eficiência operacional.

Deve-se notar que inBitmap só pode ser usado após 3.0. No 2.3, os dados de bitmap são armazenados na área de memória nativa, não no heap de memória Dalvik.

Usando o inBitmap, antes do 4.4, você só pode reutilizar áreas de memória bitmap do mesmo tamanho, mas depois do 4.4 você pode reutilizar qualquer área de memória bitmap, desde que essa memória seja maior que o bitmap que irá alocar memória. A melhor maneira aqui é usar o LRUCache para armazenar o bitmap em cache. Mais tarde, um novo bitmap aparece. Você pode encontrar o bitmap mais adequado para reutilização do cache de acordo com a versão da API para reutilizar sua área de memória.

   BitmapFactory.Options options = new BitmapFactory.Options();
        // 只解码出 outxxx参数 比如 宽、高
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(resources,id,options);
        //根据宽、高进行缩放
        int w = options.outWidth;
        int h = options.outHeight;
        //设置缩放系数
        options.inSampleSize = calcuteInSampleSize(w,h,maxW,maxH);
        if (!hasAlpha){
    
    
            options.inPreferredConfig = Bitmap.Config.RGB_565;
        }
        options.inJustDecodeBounds = false;
        //设置成能复用
        options.inMutable=true;
        options.inBitmap=reusable;

Usar cache de imagem

Há um LruCache no Android, que é uma classe de cache de dados thread-safe implementada com base no algoritmo mais lembrado e menos usado. Quando a capacidade de cache definida é excedida, os dados menos usados ​​recentemente serão eliminados primeiro. A estratégia de cache LRU de LruCache é implementado usando LinkedHashMap. E controla o tamanho do cache e elimina elementos encapsulando métodos relacionados, como get/put, mas não suporta chave e valor nulos. Podemos usar uma biblioteca de código aberto fornecida por JakeWharton github.com/JakeWharton…para implementar a lógica de nosso cache de imagem

As seções de memória e disco são omitidas.

Para ajudar todos a entender melhor a otimização de desempenho de maneira abrangente e clara, preparamos notas principais relevantes (retornando à lógica subjacente):https://qr18.cn/FVlo89

Notas básicas de otimização de desempenho:https://qr18.cn/FVlo89

Otimização de inicialização

Otimização de memória Otimização

de interface do usuário

Otimização de rede

Otimização de bitmap e otimização de compressão de imagem : Otimização de simultaneidade multi-thread e otimização de eficiência de transmissão de dados Otimização de pacote de volumehttps://qr18.cn/FVlo89




"Estrutura de monitoramento de desempenho do Android":https://qr18.cn/FVlo89

"Manual de estudo do Android Framework":https://qr18.cn/AQpN4J

  1. Processo de inicialização de inicialização
  2. Inicie o processo Zygote na inicialização
  3. Inicie o processo do SystemServer na inicialização
  4. driver de fichário
  5. Processo de inicialização do AMS
  6. O processo de inicialização do PMS
  7. Processo de inicialização do iniciador
  8. Os quatro principais componentes do Android
  9. Serviço do sistema Android - processo de distribuição do evento de entrada
  10. Análise de código-fonte do mecanismo de atualização de tela de renderização subjacente do Android
  11. Análise de código-fonte do Android na prática

Acho que você gosta

Origin blog.csdn.net/weixin_61845324/article/details/132093024
Recomendado
Clasificación