Optimización del mapa de bits para el ajuste del rendimiento de Android

fondo

En el desarrollo de Android, cargar demasiadas imágenes o imágenes demasiado grandes puede causar fácilmente una excepción OutOfMemoryError, que es nuestro desbordamiento de memoria común. Debido a que Android impone un límite de memoria en una sola aplicación, la memoria asignada por defecto es de solo unos pocos M (dependiendo de los diferentes sistemas). Si la imagen cargada está en un formato de compresión como JPG (JPG admite el nivel más alto de compresión, pero la compresión tiene pérdida), expandirla en la memoria ocupará mucho espacio en la memoria y es fácil que se desborde la memoria. Por lo tanto, es muy importante cargar Bitmap de manera eficiente. El mapa de bits se refiere a una imagen en Android, y el formato de la imagen es .jpg, .png, .webp y otros formatos comunes.

Cómo elegir un formato de imagen

Un principio: bajo la premisa de que la imagen no se distorsione visualmente, reducir al máximo el tamaño

Los formatos de imagen más utilizados actualmente en Android son png, jpeg y webp

  • png: formato de imagen comprimida sin pérdidas, admite canal alfa, el material de imagen cortado de Android adopta principalmente este formato
  • jpeg: formato de imagen comprimida con pérdida, no admite fondo transparente, adecuado para compresión de imágenes grandes con colores intensos como fotos, no adecuado para logotipo
  • webp: es un formato de imagen que proporciona compresión con pérdida y compresión sin pérdida al mismo tiempo. Se deriva del formato de codificación de video VP8. Según el sitio web oficial de Google, webp sin pérdida es un 26% más pequeño que png en promedio, y webp con pérdida es 25 % más pequeño que jpeg en promedio ~ 34 %, lossless webp es compatible con el canal alfa, lossy webp también es compatible bajo ciertas condiciones, lossy webp es compatible después de Android 4.0 (API 14), lossless y transparente es compatible con Android 4.3 (API18)

El uso de webp puede reducir efectivamente el espacio en disco ocupado por la imagen mientras se mantiene la claridad de la imagen.

Compresión de imagen

La compresión de imágenes se puede considerar desde tres aspectos:

  1. calidad

    La compresión de calidad no cambiará el tamaño de la imagen en la memoria, solo reducirá el tamaño del espacio en disco ocupado por la imagen, porque la compresión de calidad no cambiará la resolución de la imagen y el tamaño de la imagen en la memoria se basa en un píxel de ancho alto Además, dado que png es una compresión sin pérdidas, la configuración de la calidad no es vá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. Tasa de muestreo

    La compresión de frecuencia de muestreo consiste en reducir la resolución de la imagen mediante la configuración de BitmapFactory.Options.inSampleSize, lo que reduce el espacio en disco y el tamaño de la memoria que ocupa la imagen.

    El conjunto inSampleSize hará que el ancho y el alto de la imagen comprimida sean 1/inSampleSize, y el tamaño total se convertirá en un cuarto del inSampleSize de la imagen original. Por supuesto, hay algunos puntos a tener en cuenta:

    • inSampleSize menor o igual a 1 se procesará como 1

    • inSampleSize solo se puede establecer en el cuadrado de 2. Si no es el cuadrado de 2, finalmente se reducirá al cuadrado más cercano de 2. Por ejemplo, si se establece en 7, se comprimirá en 4 y si se establece en 15, se comprimirá en 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. zoom

    Reduzca el tamaño del espacio en disco y el tamaño de la memoria de la imagen al reducir los píxeles de la imagen, que se pueden usar para almacenar en caché las miniaturas.

 /**
     *  缩放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 la biblioteca JPEG para comprimir imágenes usando el algoritmo Huffman en la capa jni

El motor de imágenes de Android utiliza una versión castrada del motor skia, que elimina el algoritmo de Huffman en la compresión de imágenes.

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);
}

Debido a que involucra la parte jni, solo publicaré el código usado por el momento, y escribiré algunos blogs sobre la parte jni para compartir con ustedes más adelante.

Las imágenes de configuración se pueden reutilizar

La multiplexación de imágenes se refiere principalmente a la multiplexación de bloques de memoria. No es necesario solicitar una nueva pieza de memoria para este mapa de bits, lo que evita la asignación y recuperación de memoria, lo que mejora la eficiencia operativa.

Cabe señalar que inBitmap solo se puede usar después de 3.0. En 2.3, los datos de mapa de bits se almacenan en el área de memoria nativa, no en el montón de memoria de Dalvik.

Usando inBitmap, antes de 4.4, solo puede reutilizar áreas de memoria de mapa de bits del mismo tamaño, pero después de 4.4 puede reutilizar cualquier área de memoria de mapa de bits, siempre que esta memoria sea más grande que el mapa de bits que asignará memoria. La mejor manera aquí es usar LRUCache para almacenar en caché el mapa de bits. Más tarde, llega un nuevo mapa de bits. Puede encontrar el mapa de bits más adecuado para reutilizar desde el caché de acuerdo con la versión de la API para reutilizar su área de memoria.

   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 caché de imágenes

Hay un LruCache en Android, que es una clase de caché de datos segura para subprocesos implementada en función del algoritmo más recordado y menos utilizado. Cuando se supera la capacidad de caché establecida, los datos utilizados menos recientemente se eliminarán primero. La estrategia de caché LRU de LruCache se realiza utilizando LinkedHashMap y controla el tamaño de la memoria caché y elimina elementos encapsulando métodos relacionados como get/put, pero no admite claves y valores nulos. Podemos usar una biblioteca de código abierto proporcionada por JakeWharton github.com/JakeWharton…para implementar la lógica de nuestro caché de imágenes

Se omiten las secciones de memoria y disco.

Para ayudar a todos a comprender mejor la optimización del rendimiento de una manera completa y clara, hemos preparado notas principales relevantes (volviendo a la lógica subyacente):https://qr18.cn/FVlo89

Notas básicas de optimización del rendimiento:https://qr18.cn/FVlo89

Optimización de inicio

Optimización de memoria Optimización

de interfaz de usuario

Optimización de red

Optimización de mapa de bits y optimización de compresión de imágenes : Optimización de concurrencia de subprocesos múltiples y optimización de eficiencia de transmisión de datos Optimización de paquetes de volumenhttps://qr18.cn/FVlo89




"Marco de supervisión del rendimiento de Android":https://qr18.cn/FVlo89

"Manual de aprendizaje del marco de trabajo de Android":https://qr18.cn/AQpN4J

  1. Proceso de inicio de arranque
  2. Inicie el proceso de Zygote en el arranque
  3. Inicie el proceso SystemServer en el arranque
  4. Conductor de carpeta
  5. Proceso de inicio de AMS
  6. El proceso de puesta en marcha del PMS
  7. Proceso de inicio del lanzador
  8. Los cuatro componentes principales de Android
  9. Servicio del sistema Android: proceso de distribución del evento de entrada
  10. Análisis del código fuente del mecanismo de actualización de la pantalla de representación subyacente de Android
  11. Análisis del código fuente de Android en la práctica

Supongo que te gusta

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