Android: BitmapFactory.decodeStream () sin memoria con un archivo de 400 KB con 2 MB de espacio libre

Mi aplicación está alcanzando un error OOM en la siguiente línea en la fuente:

image = BitmapFactory.decodeStream(assetManager.open(imgFilename)); 

Justo antes de la asignación que provoca la muerte de la aplicación con un error OOM:

 (...) 08-05 21:22:12.443: I/dalvikvm-heap(2319): Clamp target GC heap from 25.056MB to 24.000MB 08-05 21:22:12.443: D/dalvikvm(2319): GC_FOR_MALLOC freed <1K, 50% free 2709K/5379K, external 18296K/19336K, paused 58ms 08-05 21:22:14.513: D/dalvikvm(2319): GC_EXTERNAL_ALLOC freed <1K, 50% free 2709K/5379K, external 18296K/19336K, paused 101ms 08-05 21:22:14.903: I/dalvikvm-heap(2319): Clamp target GC heap from 25.073MB to 24.000MB 08-05 21:22:14.903: D/dalvikvm(2319): GC_FOR_MALLOC freed 0K, 50% free 2709K/5379K, external 18312K/19336K, paused 53ms 08-05 21:22:22.843: D/ddm-heap(2319): Heap GC request 08-05 21:22:22.963: I/dalvikvm-heap(2319): Clamp target GC heap from 25.073MB to 24.000MB 08-05 21:22:22.963: D/dalvikvm(2319): threadid=1: still suspended after undo (sc=1 dc=1) 08-05 21:22:22.963: D/dalvikvm(2319): GC_EXPLICIT freed 1K, 50% free 2710K/5379K, external 18312K/19336K, paused 116ms 

DDMS informa una imagen similar sobre el estado del montón:

 Heap Size: 5.254 MB Allocated: 2.647 MB Free: 2.607 MB %Used: 50.38% #Objects 49,028 

Un solo paso sobre esta línea produce un error OOM:

 08-05 21:26:04.783: D/dalvikvm(2319): GC_EXTERNAL_ALLOC freed decode returned false 
  1. Se informa que el tamaño del archivo al que hace referencia “imgFileName” es <400K en Windows. Entonces, ¿por qué BitmapFactory.decodeStream intenta asignar 2MB?
  2. ¿Por qué hay un error OOM cuando parece que hay suficiente espacio libre?

Esta aplicación apunta a Android 2.2 y versiones posteriores.

¡Gracias por adelantado!

La biblioteca de Android no es tan inteligente para cargar imágenes, por lo que debe crear soluciones para esto.

En mis pruebas, Drawable.createFromStream usa más memoria que BitmapFactory.decodeStream .

Puede cambiar la combinación de colores para reducir la memoria (RGB_565), pero la imagen también perderá calidad:

 BitmapFactory.Options options = new BitmapFactory.Options(); options.inPreferredConfig = Config.RGB_565; Bitmap bitmap = BitmapFactory.decodeStream(stream, null, options); 

Referencia: http://developer.android.com/reference/android/graphics/Bitmap.Config.html

También puede cargar una imagen escalada, lo que disminuirá mucho el uso de la memoria, pero debe conocer sus imágenes para no perder demasiada calidad.

 BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize = 2; Bitmap bitmap = BitmapFactory.decodeStream(stream, null, options); 

Referencia: http://developer.android.com/reference/android/graphics/BitmapFactory.Options.html

Para definir dinámicamente inSampleSize, es posible que desee conocer el tamaño de la imagen para tomar su decisión:

 BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; bitmap = BitmapFactory.decodeStream(stream, null, options); int imageHeight = options.outHeight; int imageWidth = options.outWidth; options.inJustDecodeBounds = false; // recreate the stream // make some calculation to define inSampleSize options.inSampleSize = ?; Bitmap bitmap = BitmapFactory.decodeStream(stream, null, options); 

Puede personalizar inSampleSize de acuerdo con el tamaño de pantalla del dispositivo. Para obtener el tamaño de pantalla, puede hacer:

 DisplayMetrics metrics = new DisplayMetrics(); ((Activity) activity).getWindowManager().getDefaultDisplay().getMetrics(metrics); int screenWidth = metrics.widthPixels; int screenHeight =metrics.heightPixels; 

Otros tutoriales: – http://developer.android.com/training/displaying-bitmaps/load-bitmap.htmlhttp://developer.android.com/training/displaying-bitmaps/index.html

Consulte esto para obtener una guía sobre cómo cargar mapas de bits grandes de manera más eficiente:

http://developer.android.com/training/displaying-bitmaps/load-bitmap.html

Un archivo de imagen de 400 KB puede ocupar fácilmente entre 5 y 10 MB de RAM.

El tamaño del archivo en el disco no coincide necesariamente con el tamaño del archivo en la memoria. Es probable que el archivo esté comprimido, lo que no ocurrirá cuando se decodifique. Debe tenerlo en cuenta en su cálculo.

Multiplique el tamaño de la imagen (ancho x alto) por la profundidad de color de la imagen para obtener el tamaño en la memoria de la imagen.

Básicamente puedes resolver tu problema intentando escalar tu Bitmap y verás reducido el consumo de memoria. Para hacerlo, puedes copiar el método que se muestra aquí .

Además, hay una página dedicada en Android Developeres que podría ayudarlo a comprender mejor cómo cargar mapas de bits grandes. Eche un vistazo a la documentación oficial.

Resolví el problema OutOfMemoryError usando el método siguiente

 private Bitmap downloadImageBitmap(String sUrl) { this.url = sUrl; bitmap = null; try { BitmapFactory.Options options = new BitmapFactory.Options(); for (options.inSampleSize = 1; options.inSampleSize < = 32; options.inSampleSize++) { InputStream inputStream = new URL(sUrl).openStream(); try { bitmap = BitmapFactory.decodeStream(inputStream, null, options); inputStream.close(); break; } catch (OutOfMemoryError outOfMemoryError) { } } } catch (Exception e) { e.printStackTrace(); } return bitmap; } 

Si bien las respuestas anteriores son obviamente correctas, una mejor práctica es también establecer explícitamente la propiedad bitmap / src de ImageView en null , cuando ya no se usan, principalmente cuando se destruye tu actividad. Cualquier otro recurso de trabajo pesado (texto grande, audio, video) etc. también puede anularse. Esto garantiza que los recursos se liberen de forma instantánea y no esperen a que se recopile el GC.

Cambio el tamaño de la muestra en 2. Mi problema está resuelto. Pero asegúrese de que la calidad de la imagen no se destruya.