android - caused - java lang outofmemoryerror bitmapfactory decodestream
Android: BitmapFactory.decodeStream() sin memoria con un archivo de 400 KB con 2 MB de espacio libre (7)
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 <1K, 50% free 2710K/5379K, external 18312K/19336K, paused 57ms
08-05 21:26:05.023: E/dalvikvm-heap(2319): 2097152-byte external allocation too large for this process.
08-05 21:26:05.163: I/dalvikvm-heap(2319): Clamp target GC heap from 25.073MB to 24.000MB
08-05 21:26:05.163: E/GraphicsJNI(2319): VM won''t let us allocate 2097152 bytes
08-05 21:26:05.163: D/dalvikvm(2319): GC_FOR_MALLOC freed 0K, 50% free 2710K/5379K, external 18312K/19336K, paused 30ms
08-05 21:26:05.283: D/skia(2319): --- decoder->decode returned false
- 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?
- ¿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!
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 here .
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 http://developer.android.com/training/displaying-bitmaps/load-bitmap.html oficial.
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.
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.
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.html - http://developer.android.com/training/displaying-bitmaps/index.html
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.
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;
}