BitmapFactory.decodeStream sin memoria a pesar de utilizar tamaño de muestra reducido

He leído muchas publicaciones relacionadas con problemas de asignación de memoria con mapas de bits de deencoding, pero aún no puedo encontrar la solución al siguiente problema incluso después de usar el código proporcionado en el sitio web oficial.

Aquí está mi código:

public static Bitmap decodeSampledBitmapFromResource(InputStream inputStream, int reqWidth, int reqHeight) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int len; try { while ((len = inputStream.read(buffer)) > -1) { baos.write(buffer, 0, len); } baos.flush(); InputStream is1 = new ByteArrayInputStream(baos.toByteArray()); InputStream is2 = new ByteArrayInputStream(baos.toByteArray()); final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeStream(is1, null, options); options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); options.inPurgeable = true; options.inInputShareable = true; options.inJustDecodeBounds = false; options.inPreferredConfig = Bitmap.Config.ARGB_8888; return BitmapFactory.decodeStream(is2, null, options); } catch (Exception e) { e.printStackTrace(); return null; } } public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) { // Raw height and width of image final int height = options.outHeight; final int width = options.outWidth; int inSampleSize = 1; if (height > reqHeight || width > reqWidth) { // Calculate ratios of height and width to requested height and width final int heightRatio = Math.round((float) height / (float) reqHeight); final int widthRatio = Math.round((float) width / (float) reqWidth); // 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; } bitmap = decodeSampledBitmapFromResource(inputStream, 600, 600); 

Obtengo “Error de falta de memoria en una asignación 3250016 bytes” en esta línea:

 return BitmapFactory.decodeStream(is2, null, options); 

Me parece que 3.2 MB es lo suficientemente pequeño para ser asignado. ¿Dónde estoy equivocado? ¿Como puedo resolver esto?

EDITAR

Después de analizar esta solución AQUÍ por N-Joy, funciona bien con el tamaño requerido 300 pero mi tamaño requerido es 800, así que sigo recibiendo el error.

El método decodeSampledBitmapFromResource no es eficiente desde el punto de vista de la memoria porque usa 3 flujos: ByteArrayOutputStream baos, ByteArrayInputStream is1 y ByteArrayInputStream is2, cada uno de ellos almacena los mismos datos de transmisión de la imagen (una matriz de bytes para cada uno).

Y cuando pruebo con mi dispositivo (LG nexus 4) para decodificar una imagen de 2560×1600 en la tarjeta SD para alcanzar el tamaño 800, se necesita algo como esto:

 03-13 15:47:52.557: E/DecodeBitmap(11177): dalvikPss (beginning) = 1780 03-13 15:47:53.157: E/DecodeBitmap(11177): dalvikPss (decoding) = 26393 03-13 15:47:53.548: E/DecodeBitmap(11177): dalvikPss (after all) = 30401 time = 999 

Podemos ver: demasiada memoria asignada (28.5 MB) solo para decodificar 4096000 una imagen de píxel.

Solución : leemos el InputStream y almacenamos los datos directamente en una matriz de bytes y usamos esta matriz de bytes para el trabajo de descanso.
Código de muestra:

 public Bitmap decodeSampledBitmapFromResourceMemOpt( InputStream inputStream, int reqWidth, int reqHeight) { byte[] byteArr = new byte[0]; byte[] buffer = new byte[1024]; int len; int count = 0; try { while ((len = inputStream.read(buffer)) > -1) { if (len != 0) { if (count + len > byteArr.length) { byte[] newbuf = new byte[(count + len) * 2]; System.arraycopy(byteArr, 0, newbuf, 0, count); byteArr = newbuf; } System.arraycopy(buffer, 0, byteArr, count, len); count += len; } } final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeByteArray(byteArr, 0, count, options); options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); options.inPurgeable = true; options.inInputShareable = true; options.inJustDecodeBounds = false; options.inPreferredConfig = Bitmap.Config.ARGB_8888; int[] pids = { android.os.Process.myPid() }; MemoryInfo myMemInfo = mAM.getProcessMemoryInfo(pids)[0]; Log.e(TAG, "dalvikPss (decoding) = " + myMemInfo.dalvikPss); return BitmapFactory.decodeByteArray(byteArr, 0, count, options); } catch (Exception e) { e.printStackTrace(); return null; } } 

El método que hace el cálculo:

 public void onButtonClicked(View v) { int[] pids = { android.os.Process.myPid() }; MemoryInfo myMemInfo = mAM.getProcessMemoryInfo(pids)[0]; Log.e(TAG, "dalvikPss (beginning) = " + myMemInfo.dalvikPss); long startTime = System.currentTimeMillis(); FileInputStream inputStream; String filePath = Environment.getExternalStorageDirectory() .getAbsolutePath() + "/test2.png"; File file = new File(filePath); try { inputStream = new FileInputStream(file); // mBitmap = decodeSampledBitmapFromResource(inputStream, 800, 800); mBitmap = decodeSampledBitmapFromResourceMemOpt(inputStream, 800, 800); ImageView imageView = (ImageView) findViewById(R.id.image); imageView.setImageBitmap(mBitmap); } catch (FileNotFoundException e) { e.printStackTrace(); } myMemInfo = mAM.getProcessMemoryInfo(pids)[0]; Log.e(TAG, "dalvikPss (after all) = " + myMemInfo.dalvikPss + " time = " + (System.currentTimeMillis() - startTime)); } 

Y el resultado:

 03-13 16:02:20.373: E/DecodeBitmap(13663): dalvikPss (beginning) = 1823 03-13 16:02:20.923: E/DecodeBitmap(13663): dalvikPss (decoding) = 18414 03-13 16:02:21.294: E/DecodeBitmap(13663): dalvikPss (after all) = 18414 time = 917 

Este es un problema común que el usuario normalmente enfrenta al jugar con grandes mapas de bits y hay muchas preguntas discutidas en el sitio, aquí , aquí , aquí y aquí y muchas más, aunque el usuario no puede manipular la solución exacta.

Me encontré con una biblioteca en algún momento atrás que maneja bitmaps sin problemas y otros enlaces que enumeré a continuación. ¡Espero que esto ayude!

batido-Biblioteca

Android-BitmapCache

Solución Android-Universal-Image-Loader para OutOfMemoryError: el tamaño del bitmap excede el presupuesto de VM

ARGB_8888 usa más memoria ya que toma el valor de color Alpha, así que mi sugerencia es usar RGB_565 como se indica AQUÍ

Nota: la calidad será un poco baja en comparación con ARGB_8888 .

Probablemente estés aferrado a las referencias de mapas de bits anteriores. Supongo que estás ejecutando este código varias veces y nunca ejecutas bitmap.recycle() . La memoria inevitablemente se agotará.

Tuve muchos problemas con el uso de la memoria de bitmap.

Resultados:

  • La mayoría de los dispositivos tienen memoria Heap limitada para gráficos, la mayoría de los dispositivos pequeños están limitados a 16 MB para aplicaciones generales, no solo su aplicación
  • Utilice mapas de bits de 4 bit u 8 bit o 16 bit si corresponde
  • Intente dibujar formas desde cero, omita bitmaps si es posible.

Use WebView para cargar dinámicamente tantas imágenes como desee, está diseñado con NDK (bajo nivel), por lo que no tiene restricciones de memoria GDI. Funciona sin problemas y rápido 🙂

Los problemas de memoria insuficiente cuando se decodifican bitmaps no suelen estar vinculados con el tamaño de imagen que está decodificando. Por supuesto, si intentas abrir una imagen 5000x5000px, fallarás con OutOfMemoryError, pero con el tamaño de 800x800px es totalmente razonable y debería funcionar bien.

Si su dispositivo está sin memoria con una imagen de 3.2 MB, es probable que esté perdiendo contexto en algún lugar de la aplicación.

Es la primera parte de esta publicación :

Creo que el problema no está en su diseño, el problema está en otro lugar en su código. y probablemente estés perdiendo contexto en alguna parte .

Lo que significa es que está utilizando el contexto de actividad en componentes que no deberían, evitando que sean basura. Debido a que los componentes a menudo se mantienen en las actividades, esas actividades no son GC y su almacenamiento en java crecerá muy rápido y su aplicación se bloqueará en un momento u otro.

Como dijo Raghunandan, deberás usar MAT para encontrar qué Actividad / Componente se retiene y eliminar la fuga de contexto.

La mejor forma que encontré para detectar fuga de contexto es el cambio de orientación. Por ejemplo, rote su ActivityMain varias veces, ejecute MAT y verifique si solo tiene una instancia de ActivityMain. Si tiene varios (tanto como los cambios de rotación) significa que hay una fuga de contexto.

Encontré hace años un buen tutorial sobre el uso de MAT . Tal vez hay uno mejor ahora.

Otras publicaciones sobre pérdidas de memoria:

Android – pérdida de memoria o?

Error de falta de memoria en el emulador de Android, pero no en el dispositivo

Eche un vistazo a este video. http://www.youtube.com/watch?v=_CruQY55HOk . No use system.gc () como se sugiere en el video. Use un analizador MAT para encontrar memory leaks. El bitmap devuelto es demasiado grande causando pérdida de memoria, supongo.

Parece que tienes una imagen grande para mostrar.

Puede descargar una imagen y guardarla en una tarjeta SD ( ejemplo ), luego puede usar este código para mostrar la imagen desde una tarjeta SD.

También tuve el mismo problema antes … y lo he logrado utilizando esta función, donde puedes obtener una escala del ancho y la altura requeridos.

 private Bitmap decodeFile(FileInputStream f) { try { //decode image size BitmapFactory.Options o = new BitmapFactory.Options(); o.inJustDecodeBounds = true; BitmapFactory.decodeStream(f,null,o); //Find the correct scale value. It should be the power of 2. final int REQUIRED_SIZE=70; int width_tmp=o.outWidth, height_tmp=o.outHeight; int scale=1; while(true) { if(width_tmp/2 

y consulte Memory Leak Error Android y error al cargar imágenes en gridview android