Excepción OutOfMemory al cargar mapa de bits desde almacenamiento externo

En mi aplicación cargar un par de imágenes de archivos JPEG y PNG. Cuando coloco todos esos archivos en el directorio de activos y lo carga de esta manera, todo está bien:

InputStream stream = getAssets().open(path); Bitmap bitmap = BitmapFactory.decodeStream(stream, null, null); stream.close(); return new BitmapDrawable(bitmap); 

Pero cuando trato de cargar las mismas imágenes exactas de la tarjeta SD, me sale una excepción OutOfMemory!

 InputStream stream = new FileInputStream("/mnt/sdcard/mydata/" + path); Bitmap bitmap = BitmapFactory.decodeStream(stream, null, null); stream.close(); return new BitmapDrawable(bitmap); 

Esto es lo que obtengo en el registro:

 11-05 00:53:31.003: ERROR/dalvikvm-heap(13183): 827200-byte external allocation too large for this process. 11-05 00:53:31.003: ERROR/GraphicsJNI(13183): VM won't let us allocate 827200 bytes ... 11-05 00:53:31.053: ERROR/AndroidRuntime(13183): Caused by: java.lang.OutOfMemoryError: bitmap size exceeds VM budget 11-05 00:53:31.053: ERROR/AndroidRuntime(13183): at android.graphics.BitmapFactory.nativeDecodeStream(Native Method) ... 

¿Por qué puede suceder esto?

UPDATE: Probado ambos en el dispositivo real – parece que no puedo cargar más de 12 MB de mapas de bits en lo que se llama "memoria externa" (esto no es una tarjeta SD).

Probablemente no hay nada malo con el uso de su API, supongo que todo lo que podemos hacer es inferir que el uso de AssetManager implica menos detrás de las escenas de asignación de montón que abrir un archivo aleatorio de la tarjeta SD.

800KB es una asignación seria en el libro de nadie … esto será sin duda para los píxeles de imagen descomprimidos. Dado que usted sabe el tamaño de la imagen, ¿qué profundidad es? Si es 32bpp, intente reemplazarlo mediante inPreferredConfig .

He intentado todos los enfoques mencionados aquí y en otros recursos, pero llegué a la conclusión de que establecer la referencia de ImageView a null resolverá el problema:

  public Bitmap getimage(String path ,ImageView iv) { //iv is passed to set it null to remove it from external memory iv=null; InputStream stream = new FileInputStream("/mnt/sdcard/mydata/" + path); Bitmap bitmap = BitmapFactory.decodeStream(stream, null, null); stream.close(); stream=null; return bitmap; } 

& ¡estás listo!

Nota: Aunque puede resolver el problema anterior, pero te sugiero que compruebes la carga de imagen optimizada de Tom van Zummeren .

Y también revisa SoftReference : Todas las SoftReferences que apuntan a objetos suavemente accesibles están garantizados para ser borrados antes de que la VM lance un OutOfMemoryError.

  • Al hacer un montón con mapas de bits, no depurar la aplicación – sólo ejecutarlo. El depurador dejará fugas de memoria.
  • Los mapas de bits son muy caros. Si es posible, reduzca la carga en la carga creando BitmapFactory.Options y estableciendo inSampleSize en> 1.

EDITAR: Además, asegúrese de verificar si su aplicación tiene fugas de memoria. La filtración de un mapa de bits (tener static bits static es una excelente manera de hacerlo) agotará rápidamente su memoria disponible.

 The best solution i found and edited according to my need public static Bitmap getImageBitmap(String path) throws IOException{ // Allocate files and objects outside of timingoops File file = new File(thumbpath); RandomAccessFile in = new RandomAccessFile(file, "rws"); final FileChannel channel = in.getChannel(); final int fileSize = (int)channel.size(); final byte[] testBytes = new byte[fileSize]; final ByteBuffer buff = ByteBuffer.allocate(fileSize); final byte[] buffArray = buff.array(); @SuppressWarnings("unused") final int buffBase = buff.arrayOffset(); // Read from channel into buffer, and batch read from buffer to byte array; long time1 = System.currentTimeMillis(); channel.position(0); channel.read(buff); buff.flip(); buff.get(testBytes); long time1 = System.currentTimeMillis(); Bitmap bmp = Bitmap_process(buffArray); long time2 = System.currentTimeMillis(); System.out.println("Time taken to load: " + (time2 - time1) + "ms"); return bmp; } public static Bitmap Bitmap_process(byte[] buffArray){ BitmapFactory.Options options = new BitmapFactory.Options(); options.inDither=false; //Disable Dithering mode options.inPurgeable=true; //Tell to gc that whether it needs free memory, the Bitmap can be cleared options.inInputShareable=true; //Which kind of reference will be used to recover the Bitmap data after being clear, when it will be used in the future options.inTempStorage=new byte[32 * 1024]; //Allocate some temporal memory for decoding options.inSampleSize=1; Bitmap imageBitmap = BitmapFactory.decodeByteArray(buffArray, 0, buffArray.length, options); return imageBitmap; } 

Este es un problema bastante común que todos nos enfrentamos al cargar imágenes desde el sdcard.

La solución que encontré fue usar inJustDecodeBounds primero mientras carga la imagen usando decodeFileDescriptor . Eso realmente no decodificar la imagen, pero dar el tamaño de la imagen. Ahora puedo escalarlo apropiadamente (usando las opciones) para cambiar el tamaño de la imagen para el área de visualización. Es necesario porque la memoria baja en el teléfono puede ser fácilmente asumido por su imagen de 5MP. Esto creo que es la solución más elegante.

Hay dos problemas aquí….

  • La memoria de mapa de bits no está en el montón de VM, sino en el montón nativo – vea BitmapFactory OOM me conduce nueces
  • La recolección de basura para el montón nativo es más vaga que el montón de VM, por lo que necesitas ser muy agresivo para hacer bitmap.recycle y bitmap = null cada vez que pases a través de Activación onPause o onDestroy

En lugar de cargar directamente desde la tarjeta SD, ¿por qué no mover la imagen al caché en el almacenamiento interno del teléfono mediante getCacheDir () o utilizar un directorio temporal para almacenar las imágenes?

Ver esto , esto en el uso de memoria externa. Además, este artículo puede ser relevante para usted.

Gracias a todos los hilos, he encontrado una solución que funciona para mí en un dispositivo real. Los trucos son todos sobre el uso

 BitmapFactory.Options opts=new BitmapFactory.Options(); opts.inSampleSize=(int)(target_size/bitmap_size); //if original bitmap is bigger 

Pero para mí esto no era suficiente. Mi imagen original (tomada de la aplicación Cámara) fue 3264×2448. La razón correcta para mí era de 3, ya que quería una imagen VGA simple de 1024×768.

Pero la configuración de inSampleSize a 3 no era suficiente: aún fuera de la excepción de memoria. Así que al final opté por un enfoque iterativo: Empecé desde el tamaño correcto calculado, y aumentar hasta que deje de tener una excepción MOO. Para mí fue en la muestra de 4.

 // Decode with inSampleSize BitmapFactory.Options o2 = new BitmapFactory.Options(); // o2.inSampleSize = scale; float trueScale = o.outWidth / 1024; o2.inPurgeable = true; o2.inDither = false; Bitmap b = null; do { o2.inSampleSize = (int) trueScale; Log.d(TAG, "Scale is " + trueScale); try { b = BitmapFactory.decodeStream(new FileInputStream(f), null, o2); } catch (OutOfMemoryError e) { Log.e(TAG,"Error decoding image at sampling "+trueScale+", resampling.."+e); System.gc(); try { Thread.sleep(50); } catch (InterruptedException e1) { e1.printStackTrace(); } } trueScale += 1; } while (b==null && trueScale < 10); return b; 

No debe depender del GC para reciclar su memoria de mapa de bits. Debe reciclar claramente el mapa de bits cuando no es necesario.

Consulte el método Bitmap:

Void recycle () Libera la memoria asociada con los píxeles de este mapa de bits y marca el bitmap como "dead", lo que significa que lanzará una excepción si se llama a getPixels () o setPixels () y no dibujará nada.

Uno de los errores más comunes que encontré desarrollando aplicaciones de Android es el error "java.lang.OutOfMemoryError: tamaño de mapa de bits supera el presupuesto de VM". Encontré este error frecuentemente en actividades que utilizan muchos mapas de bits después de cambiar la orientación: la actividad se destruye, se crea de nuevo y los diseños se "inflan" desde el XML que consume la memoria VM disponible para mapas de bits.

Los mapas de bits del diseño de la actividad anterior no se desasignan correctamente por el recolector de elementos no utilizados porque han cruzado referencias a su actividad. Después de muchos experimentos encontré una solución bastante buena para este problema.

Primero, establezca el atributo "id" en la vista principal de su diseño XML:

  <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:id="@+id/RootView" > ... 

A continuación, en el método onDestroy () de su actividad, llame al método unbindDrawables () pasando un refence a la vista primaria y, a continuación, haga un System.gc ()

  @Override protected void onDestroy() { super.onDestroy(); unbindDrawables(findViewById(R.id.RootView)); System.gc(); } private void unbindDrawables(View view) { if (view.getBackground() != null) { view.getBackground().setCallback(null); } if (view instanceof ViewGroup) { for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) { unbindDrawables(((ViewGroup) view).getChildAt(i)); } ((ViewGroup) view).removeAllViews(); } } 

Este método unbindDrawables () explora el árbol de vistas de forma recursiva y:

  1. Elimina las devoluciones de llamada en todos los elementos de fondo
  2. Elimina niños en cada grupo de vistas

Prueba esto de otra manera …

 Bitmap bmpOrignal = BitmapFactory.decodeFile("/sdcard/mydata/" + path"); 

Utilice el código a continuación y nunca obtendrá el siguiente error: java.lang.OutOfMemoryError: tamaño de mapa de bits supera el presupuesto de VM

  BitmapFactory.Options bounds = new BitmapFactory.Options(); bounds.inSampleSize = 4; myBitmap = BitmapFactory.decodeFile(imgFile.getAbsolutePath(), bounds); picturesView.setImageBitmap(myBitmap); 

Permite que inSampleSize redimensione la imagen de lectura final. GetLength () de AssetFileDescriptor permite obtener el tamaño del archivo.

Puede variar inSampleSize de acuerdo a getLength () para evitar OutOfMemory de esta manera:

 private final int MAX_SIZE = 500000; public Bitmap readBitmap(Uri selectedImage) { Bitmap bm = null; AssetFileDescriptor fileDescriptor = null; try { fileDescriptor = this.getContentResolver().openAssetFileDescriptor(selectedImage,"r"); long size = fileDescriptor.getLength(); BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize = (int) (size / MAX_SIZE); bm = BitmapFactory.decodeFileDescriptor(fileDescriptor.getFileDescriptor(), null, options); } catch (Exception e) { e.printStackTrace(); } finally { try { if(fileDescriptor != null) fileDescriptor.close(); } catch (IOException e) {} } return bm; } 
  • OutOfMemoryError: Usando una imagen que es 1080 * 1920 haga la página de bienvenida
  • Java.lang.OutOfMemoryError: tamaño de mapa de bits supera el presupuesto de VM - Android
  • Android Throwing OutOfMemoryError "Error al asignar una asignación de 164 bytes con 44 bytes libres y 44B hasta OOM" (caso recursivo)
  • Error de OutOfMemory de Android y LruCache
  • Java.lang.OutOfMemoryError en almacenar imágenes en sqlite db
  • Métricas de memoria en aplicaciones Android
  • Android Drawable vs Asset en rendimiento de carga de imágenes
  • Fuera de la excepción de memoria en gson.fromJson ()
  • Android: java.lang.OutOfMemoryError: falla en la creación del hilo
  • SplashScreen con la imagen PNG conduce a Android.Views.InflateException seguido de OutOfMemory
  • Android claro hilo webview, libre de memoria, evitar OutOfMemoryError
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.