El uso generalizado del mapa de bits de Android no produce una "optimización de la carga de recursos de imágenes grandes" en OOM

He estado trabajando en un navegador de imágenes estos días, pero OOM ocurrió al cargar una gran cantidad de imágenes. Para resolver este problema que generalmente se encuentra al cargar imágenes, verifiqué el documento oficial de Google y registré el conocimiento que aprendí. ayuda.

Primero echemos un vistazo a por qué deberíamos prestar atención a la optimización del uso de mapas de bits: 
1. Los dispositivos móviles suelen tener recursos de sistema limitados. Por ejemplo, los dispositivos Android pueden limitar cada aplicación a un máximo de 16M. En otras palabras, su aplicación debe estar optimizada para ocupar menos de 16M de memoria. 
2. El mapa de bits es la mayor parte de la memoria. Por ejemplo, Galaxy Nexus puede tomar una foto con píxeles de hasta 2592x1936. Luego, el mapa de bits usa ARGB_8888 para analizar los colores de forma predeterminada. Entonces esta imagen ocupa aproximadamente 19M (2592 * 1936 * 4 bytes). De esta manera, el límite de memoria en el ejemplo que acaba de hacer se superó inmediatamente. 
3. La interfaz de usuario de Android utiliza a menudo mapas de bits de forma extensa. Por ejemplo, listview, gridview, viewpager. Generalmente, se muestran muchos mapas de bits en la página. También incluye la parte que no se muestra pero a la que se deslizará.

En muchos casos, la imagen que se muestra en nuestra interfaz suele ser inferior a los requisitos reales de píxeles de la imagen. Y el uso de estas imágenes de píxeles altos en la interfaz con requisitos de píxeles bajos no tendrá la ventaja del efecto de visualización. Por el contrario, consumirá mucha memoria y afectará la eficiencia, porque este proceso requiere escalado.

BitmapFactory proporciona una serie de métodos de análisis para diferentes fuentes de datos (decodeByteArray (), decodeFile (), decodeResource (), etc.). Estos métodos asignarán memoria al construir el mapa de bits, por lo que es fácil generar OOM. Cada método de análisis proporciona un parámetro opcional BitmapFactory.Options para definir las propiedades del análisis. Puede evitar la asignación de memoria durante el análisis estableciendo la propiedad inJustDecodeBounds en true. En este momento, el análisis devuelve nulo, pero outWidth, outHeigth y outMimeType se asignan a las opciones. Esta técnica le permite obtener el tamaño y tipo del mapa de bits sin asignar memoria. Mira el código:

BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;

Comprimir una imagen grande

¿Cómo podemos comprimir la imagen? Se puede lograr estableciendo el valor de inSampleSize en BitmapFactory.Options. Por ejemplo, si tenemos una imagen de 2048 * 1536 píxeles, podemos comprimir esta imagen a 512 * 384 píxeles estableciendo el valor de inSampleSize en 4. La carga original de esta imagen requiere 13 M de memoria, pero después de la compresión, solo toma 0,75 M (asumiendo que la imagen es del tipo ARGB_8888, es decir, cada píxel ocupa 4 bytes). El siguiente método puede calcular el valor apropiado de inSampleSize en función del ancho y alto de entrada:

public static int calculateInSampleSize(BitmapFactory.Options options,  
        int reqWidth, int reqHeight) {  
    // 源图片的高度和宽度  
    final int height = options.outHeight;  
    final int width = options.outWidth;  
    int inSampleSize = 1;  
    if (height > reqHeight || width > reqWidth) {  
        // 计算出实际宽高和目标宽高的比率  
        final int heightRatio = Math.round((float) height / (float) reqHeight);  
        final int widthRatio = Math.round((float) width / (float) reqWidth);  
        // 选择宽和高中最小的比率作为inSampleSize的值,这样可以保证最终图片的宽和高  
        // 一定都会大于等于目标的宽和高。  
        inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;  
    }  
    return inSampleSize;  
}
Para utilizar este método, primero debe establecer la propiedad inJustDecodeBounds de BitmapFactory.Options en true para analizar la imagen una vez. Luego, pase BitmapFactory.Options junto con el ancho y alto deseados al método calculateInSampleSize, y podrá obtener el valor inSampleSize apropiado. Después de analizar la imagen nuevamente, usando el valor de inSampleSize recién adquirido y estableciendo inJustDecodeBounds en falso, puede obtener la imagen comprimida.

public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,  
        int reqWidth, int reqHeight) {  
    // 第一次解析将inJustDecodeBounds设置为true,来获取图片大小  
    final BitmapFactory.Options options = new BitmapFactory.Options();  
    options.inJustDecodeBounds = true;  
    BitmapFactory.decodeResource(res, resId, options);  
    // 调用上面定义的方法计算inSampleSize值  
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);  
    // 使用获取到的inSampleSize值再次解析图片  
    options.inJustDecodeBounds = false;  
    return BitmapFactory.decodeResource(res, resId, options);  
} 
El siguiente código es muy simple para comprimir cualquier imagen en una miniatura de 100 * 100 y mostrarla en ImageView.

[Java]  ver llano   Copiar
  1. mImageView.setImageBitmap (  
  2.     decodeSampledBitmapFromResource (getResources (), R.id.myimage,  100 100 )); 

En segundo lugar, utilice la tecnología de almacenamiento en caché de imágenes

El siguiente es un ejemplo del uso de LruCache para almacenar imágenes en caché:

private LruCache<String, Bitmap> mMemoryCache;  
  
@Override  
protected void onCreate(Bundle savedInstanceState) {  
    // 获取到可用内存的最大值,使用内存超出这个值会引起OutOfMemory异常。  
    // LruCache通过构造函数传入缓存值,以KB为单位。  
    int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);  
    // 使用最大可用内存值的1/8作为缓存的大小。  
    int cacheSize = maxMemory / 8;  
    mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {  
        @Override  
        protected int sizeOf(String key, Bitmap bitmap) {  
            // 重写此方法来衡量每张图片的大小,默认返回图片数量。  
            return bitmap.getByteCount() / 1024;  
        }  
    };  
}  
  
public void addBitmapToMemoryCache(String key, Bitmap bitmap) {  
    if (getBitmapFromMemCache(key) == null) {  
        mMemoryCache.put(key, bitmap);  
    }  
}  
  
public Bitmap getBitmapFromMemCache(String key) {  
    return mMemoryCache.get(key);  
} 
En este ejemplo, un octavo de la memoria asignada por el sistema a la aplicación se utiliza como tamaño de caché. En teléfonos de configuración media a alta, probablemente tendrá 4 megabytes (32/8) de espacio en caché. Un GridView de pantalla completa utiliza 4 imágenes de resolución de 800x480 para rellenar, ocupará aproximadamente 1,5 megabytes de espacio (800 * 480 * 4). Por lo tanto, este tamaño de caché puede almacenar 2,5 páginas de imágenes.
Al cargar una imagen en ImageView, primero la verificará en el caché de LruCache. Si se encuentra el valor de clave correspondiente, ImageView se actualizará inmediatamente; de ​​lo contrario, se iniciará un hilo de fondo para cargar la imagen

  1. public void  loadBitmap ( int  resId, ImageView imageView) {   
  2.     final  String imageKey = String.valueOf (resId);  
  3.      mapa de bits final mapa de bits = getBitmapFromMemCache (imageKey);  
  4.     if  (mapa de bits! =  nulo ) {  
  5.         imageView.setImageBitmap (mapa de bits);  
  6.     } else {  
  7.         imageView.setImageResource(R.drawable.image_placeholder);  
  8.         BitmapWorkerTask task = new BitmapWorkerTask(imageView);  
  9.         task.execute(resId);  
  10.     }  
BitmapWorkerTask 还要把新加载的图片的键值对放到缓存中。
[java]  view plain   copy
  1. class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {  
  2.     // 在后台加载图片。  
  3.     @Override  
  4.     protected Bitmap doInBackground(Integer... params) {  
  5.         final Bitmap bitmap = decodeSampledBitmapFromResource(  
  6.                 getResources(), params[0], 100100);  
  7.         addBitmapToMemoryCache(String.valueOf(params[0]), bitmap);  
  8.         devolver  mapa de bits;  
  9.     }  
  10. }  






Supongo que te gusta

Origin blog.csdn.net/xifei66/article/details/76883449
Recomendado
Clasificación