android - studio - Excepción de OutOfMemory al cargar mapa de bits desde un almacenamiento externo




outofmemoryerror android (9)

En mi aplicación cargo un par de imágenes de archivos JPEG y PNG. Cuando coloco todos esos archivos en el directorio de activos y los cargo 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 intento cargar exactamente las mismas imágenes de la tarjeta sd, ¡obtengo una excepción de 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 pasar esto?

ACTUALIZACIÓN: Probé ambos en un dispositivo real: parece que no puedo cargar más de 12 MB de mapas de bits en lo que se denomina "memoria externa" (esto no es una tarjeta SD).


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

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


Este es un problema bastante común al que todos nos enfrentamos al cargar imágenes desde la tarjeta SD.

La solución que encontré fue usar inJustDecodeBounds primero al cargar la imagen usando decodeFileDescriptor . Eso no descodificaría realmente la imagen, pero daría 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 imagen de 5MP puede controlar fácilmente la poca memoria del teléfono. Esta creo que es la solución más elegante.


Hay dos problemas aquí....

  • La memoria de mapa de bits no se encuentra en el montón de VM, sino en el montón nativo; consulte BitmapFactory OOM que me vuelve loco
  • La recolección de basura para el montón nativo es más perezosa que el montón de la máquina virtual, por lo que debe ser bastante agresivo al hacer bitmap.recycle y bitmap = null cada vez que realice una actividad en OnPause o onDestroy

Intenta esto de otra manera ...

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

Permite a inSampleSize cambiar el tamaño de la imagen de lectura final. getLength() de AssetFileDescriptor permite obtener el tamaño del archivo.

Puede variar inSampleSize según getLength () para evitar que OutOfMemory sea así:

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;
}

Probé todos los enfoques mencionados here 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, le sugiero que verifique la carga optimizada de la imagen de Tom van Zummeren .

Y también verifique SoftReference : se SoftReference todas las SoftReferences que apuntan a objetos de fácil acceso se borrarán antes de que la máquina virtual lance un OutOfMemoryError.


Uno de los errores más comunes que encontré al desarrollar aplicaciones de Android es el error “java.lang.OutOfMemoryError: el tamaño del mapa de bits supera el presupuesto de VM”. Encontré este error frecuentemente en actividades que usan 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 de la VM disponible para los mapas de bits.

Los mapas de bits en el diseño de la actividad anterior no son desasignados correctamente por el recolector de basura 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"
     >
     ...

Luego, en el método onDestroy () de su Actividad, llame al método unbindDrawables () pasando un refence a la Vista principal y luego 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 vista recursivamente y:

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

Utilice el siguiente código y nunca obtendrá el siguiente error: java.lang.OutOfMemoryError: el tamaño del mapa de bits supera el presupuesto de la máquina virtual

              BitmapFactory.Options bounds = new BitmapFactory.Options();

              bounds.inSampleSize = 4;

              myBitmap = BitmapFactory.decodeFile(imgFile.getAbsolutePath(), bounds);

              picturesView.setImageBitmap(myBitmap);

  • Al hacer mucho con mapas de bits, no depure la aplicación, simplemente ejecútela. El depurador dejará fugas de memoria.
  • Los mapas de bits son muy caros. Si es posible, inSampleSize al cargar creando BitmapFactory.Options y configurando inSampleSize a> 1.

EDITAR: También, asegúrese de revisar su aplicación para detectar pérdidas de memoria. Fugas en un mapa de bits (tener static bits static es una excelente manera de hacerlo) agotará rápidamente la memoria disponible.







out-of-memory