2010-02-08 22 views
29

Tengo un montón de URL de imágenes. Tengo que descargar estas imágenes y mostrarlas en mi aplicación una a una. Estoy guardando las imágenes en una Colección usando SoftReferences y también en Sdcard para evitar reetiquetas y mejorar la experiencia del usuario.Manejo de mapas de bits grandes

El problema es que no sé nada sobre el tamaño de los mapas de bits. Y resulta que estoy recibiendo OutOfMemoryExceptions esporádicamente, cuando estoy usando el método BitmapFactory.decodeStream(InputStream). Por lo tanto, elegí reducir la resolución de las imágenes utilizando las opciones de BitmapFactory (tamaño de muestra = 2). Esto dio un mejor resultado: sin OOM, pero esto afecta la calidad de las imágenes más pequeñas.

¿Cómo debo manejar estos casos? ¿Hay alguna manera de disminuir la resolución de imágenes de alta resolución?

+0

El sitio oficial [Android Training] (http://developer.android.com/training/index.html) ahora tiene una clase llamada [Mostrar mapas de bits de manera eficiente] (http://developer.android.com/training/ displaying-bitmaps/index.html) con una lección, [Cargando grandes mapas de bits de manera eficiente] (http://developer.android.com/training/displaying-bitmaps/load-bitmap.html) que repasa esto. La lección [Caching Bitmaps] (http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html) también cubre algo de información sobre el almacenamiento en caché utilizando una memoria caché de referencia de disco duro en lugar de una WeakReference o SoftReference. – AdamK

Respuesta

55

Hay una opción en BitmapFactory.Options clase (una pasé por alto) llamado inJustDecodeBounds, javadoc de la cual se lee:

Si es verdadero, el descodificador devuelto nulo (sin mapa de bits), pero el a cabo ... los campos seguirán configurándose, , permitiendo que la persona que llama pueda consultar el mapa de bits sin tener que asignar la memoria a sus píxeles.

Lo usé para descubrir el tamaño real del Mapa de Bits y luego elegí la muestra hacia abajo usando la opción inSampleSize. Esto al menos evita cualquier error OOM mientras decodifica el archivo.

Referencia:
1. Handling larger Bitmaps
2. How do I get Bitmap info before I decode

+9

+1 por responder su propia pregunta cuando descubrió la respuesta; piensa en los que buscan en Google y encuentra tu pregunta, pero no contesta. – Will

+0

Realmente funciona bien si quiero obtener las dimensiones, pero obtengo un error OOM cuando quiero decodificar un archivo en mapa de bits y establecer ese mapa de bits en ImageView. Por favor dígame cómo optimizarlo o de cualquier otra manera para solucionar este problema. –

12

Lo que he hecho a mí mismo es:

  • uso inJustDecodeBounds para obtener el tamaño original de la imagen
  • tienen un fijo Superficie máxima para cargar el mapa de bits (digamos 1Mpixels)
  • compruebe si la superficie de la imagen está por debajo del límite, en caso afirmativo, cárguelo directamente
  • si no calcule el ancho y la altura ideales en los que debe cargar el mapa de bits para mantenerse por debajo de la superficie máxima (aquí hay algunas matemáticas simples que hacer). Esto le da una relación de flotación que desea aplicar al cargar el mapa de bits
  • ahora quiere traducir esta relación a una adecuada inSampleSize (una potencia de 2 que no degrada la calidad de la imagen). Puedo usar esta función:
 
int k = Integer.highestOneBit((int)Math.floor(ratio)); 
if(k==0) return 1; 
else return k; 
  • continuación, debido a que el mapa de bits se habrá cargado con una resolución ligeramente superior a la superficie máxima (porque se tenía que utilizar una menor potencia de 2), tendrá para cambiar el tamaño del mapa de bits, pero será mucho más rápido.
+0

gracias por agregar la rutina para calcular el tamaño de muestra. Aumenta el hilo con una información importante. – Samuh

11

Después de unos días que luchan para evitar todos los errores OutOfMemory que estaba recibiendo con diferentes dispositivos, creo esto:

private Bitmap getDownsampledBitmap(Context ctx, Uri uri, int targetWidth, int targetHeight) { 
    Bitmap bitmap = null; 
    try { 
     BitmapFactory.Options outDimens = getBitmapDimensions(uri); 

     int sampleSize = calculateSampleSize(outDimens.outWidth, outDimens.outHeight, targetWidth, targetHeight); 

     bitmap = downsampleBitmap(uri, sampleSize); 

    } catch (Exception e) { 
     //handle the exception(s) 
    } 

    return bitmap; 
} 

private BitmapFactory.Options getBitmapDimensions(Uri uri) throws FileNotFoundException, IOException { 
    BitmapFactory.Options outDimens = new BitmapFactory.Options(); 
    outDimens.inJustDecodeBounds = true; // the decoder will return null (no bitmap) 

    InputStream is= getContentResolver().openInputStream(uri); 
    // if Options requested only the size will be returned 
    BitmapFactory.decodeStream(is, null, outDimens); 
    is.close(); 

    return outDimens; 
} 

private int calculateSampleSize(int width, int height, int targetWidth, int targetHeight) { 
    int inSampleSize = 1; 

    if (height > targetHeight || width > targetWidth) { 

     // Calculate ratios of height and width to requested height and 
     // width 
     final int heightRatio = Math.round((float) height 
       /(float) targetHeight); 
     final int widthRatio = Math.round((float) width/(float) targetWidth); 

     // Choose the smallest ratio as inSampleSize value, this will 
     // guarantee 
     // a final image with both dimensions larger than or equal to the 
     // requested height and width. 
     inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio; 
    } 
    return inSampleSize; 
} 

private Bitmap downsampleBitmap(Uri uri, int sampleSize) throws FileNotFoundException, IOException { 
    Bitmap resizedBitmap; 
    BitmapFactory.Options outBitmap = new BitmapFactory.Options(); 
    outBitmap.inJustDecodeBounds = false; // the decoder will return a bitmap 
    outBitmap.inSampleSize = sampleSize; 

    InputStream is = getContentResolver().openInputStream(uri); 
    resizedBitmap = BitmapFactory.decodeStream(is, null, outBitmap); 
    is.close(); 

    return resizedBitmap; 
} 

Este método funciona con todos los dispositivos que he probado, pero creo que la calidad puede Sería mejor usar otro proceso del que no soy consciente.

Espero que mi código pueda ayudar a otros desarrolladores en la misma situación. También aprecio que un desarrollador experimentado pueda ayudar, dando una sugerencia sobre otro proceso para evitar perder (menos) calidad en el proceso.

+1

cosas buenas. Pero el calculateSampleSize fue incorrecto. Ahora he actualizado la publicación para solucionarlo. – Zammbi

+1

Una muy buena pieza de trabajo. ¡Gracias por compartir! – MacD

Cuestiones relacionadas