1217 votos

Extraño de problema de memoria al cargar una imagen a un objeto de mapa de bits

Tengo una vista de lista con un par de imágenes de los botones en cada fila. Cuando usted haga clic en la fila de la lista, lanza una nueva actividad. He tenido que crear mi propio pestañas debido a un problema con la cámara de diseño. La actividad que se puso en marcha para el resultado es un mapa. Si hago clic en mi botón para iniciar la previsualización de la imagen (carga de una imagen de la tarjeta SD) la aplicación de rendimientos de la actividad para el control de la actividad para el resultado de controlador para relanzar mi nueva actividad que no es más que una imagen del widget.

La previsualización de la imagen en la vista de lista que se está haciendo con el cursor y ListAdapter. Esto hace que sea bastante sencillo, pero no estoy seguro de cómo puedo poner una imagen redimensionada (es Decir. Pequeño tamaño del píxel como el src de la imagen en el botón de volar. Así que sólo he cambiado el tamaño de la imagen que salió de la cámara del teléfono.

El problema es que me sale un error de memoria insuficiente cuando se trata de volver y re-lanzamiento de la 2ª actividad.

* * ¿Hay una manera que se puede construir la lista adaptador fácilmente fila por fila, donde puedo cambiar el tamaño de la mosca (poco inteligente)? Esto sería preferible, ya que yo también tendrá que hacer algunos cambios a las propiedades de los widgets/de los elementos en cada fila como soy incapaz de seleccionar una fila con pantalla táctil, debido a problema de enfoque. (Puedo usar el rodillo de la bola.)

** Sé que puedo hacer un fuera de banda de cambiar el tamaño y la guarde de mi imagen, pero eso no es realmente lo que quiero hacer, pero algún código de ejemplo para que iba a estar bien.

Tan pronto como he desactivado la imagen en la vista de lista funcionaba bien de nuevo.

FYI: Esta es la forma en que lo estaba haciendo:

String[] from = new String[] { DBHelper.KEY_BUSINESSNAME,
                               DBHelper.KEY_ADDRESS,
                               DBHelper.KEY_CITY,
                               DBHelper.KEY_GPSLONG,
                               DBHelper.KEY_GPSLAT,
                               DBHelper.KEY_IMAGEFILENAME  + ""};
int[] to = new int[] { R.id.businessname,
                       R.id.address,
                       R.id.city,
                       R.id.gpslong,
                       R.id.gpslat,
                       R.id.imagefilename };
notes = new SimpleCursorAdapter(this, R.layout.notes_row, c, from, to);
setListAdapter(notes);

Donde R.id.imagefilename es ButtonImage.

Aquí está mi LogCat:


01-25 05:05:49.877: ERROR/dalvikvm-heap(3896): 6291456-byte external allocation too large for this process.
01-25 05:05:49.877: ERROR/(3896): VM wont let us allocate 6291456 bytes
01-25 05:05:49.877: ERROR/AndroidRuntime(3896): Uncaught handler: thread main exiting due to uncaught exception
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): java.lang.OutOfMemoryError: bitmap size exceeds VM budget
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:304)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:149)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:174)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.graphics.drawable.Drawable.createFromPath(Drawable.java:729)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.ImageView.resolveUri(ImageView.java:484)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.ImageView.setImageURI(ImageView.java:281)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.SimpleCursorAdapter.setViewImage(SimpleCursorAdapter.java:183)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.SimpleCursorAdapter.bindView(SimpleCursorAdapter.java:129)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.CursorAdapter.getView(CursorAdapter.java:150)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.AbsListView.obtainView(AbsListView.java:1057)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.ListView.makeAndAddView(ListView.java:1616)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.ListView.fillSpecific(ListView.java:1177)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.ListView.layoutChildren(ListView.java:1454)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.AbsListView.onLayout(AbsListView.java:937)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1119)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.layoutHorizontal(LinearLayout.java:1108)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.onLayout(LinearLayout.java:922)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.FrameLayout.onLayout(FrameLayout.java:294)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1119)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.layoutVertical(LinearLayout.java:999)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.onLayout(LinearLayout.java:920)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.FrameLayout.onLayout(FrameLayout.java:294)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.ViewRoot.performTraversals(ViewRoot.java:771)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.ViewRoot.handleMessage(ViewRoot.java:1103)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.os.Handler.dispatchMessage(Handler.java:88)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.os.Looper.loop(Looper.java:123)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.app.ActivityThread.main(ActivityThread.java:3742)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at java.lang.reflect.Method.invokeNative(Native Method)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at java.lang.reflect.Method.invoke(Method.java:515)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:739)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:497)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at dalvik.system.NativeStart.main(Native Method)
01-25 05:10:01.127: ERROR/AndroidRuntime(3943): ERROR: thread attach failed

También tengo un nuevo error al mostrar una imagen:

01-25 22:13:18.594: DEBUG/skia(4204): xxxxxxxxxxx jpeg error 20 Improper call to JPEG library in state %d
01-25 22:13:18.604: INFO/System.out(4204): resolveUri failed on bad bitmap uri: 
01-25 22:13:18.694: ERROR/dalvikvm-heap(4204): 6291456-byte external allocation too large for this process.
01-25 22:13:18.694: ERROR/(4204): VM won't let us allocate 6291456 bytes
01-25 22:13:18.694: DEBUG/skia(4204): xxxxxxxxxxxxxxxxxxxx allocPixelRef failed

878voto

Fedor Puntos 29890

Para solucionar OutOfMemory debe hacer algo parecido a esto:

BitmapFactory.Options options=new BitmapFactory.Options();
options.inSampleSize = 8;
Bitmap preview_bitmap=BitmapFactory.decodeStream(is,null,options);

Este inSampleSize opción reduce el consumo de memoria.

He aquí un método completo. Primero lee el tamaño de la imagen sin descifrar el contenido en sí mismo. A continuación, se encuentra la mejor inSampleSize valor, debe ser una potencia de 2. Y, finalmente, la imagen es decodificado.

//decodes image and scales it to reduce memory consumption
private Bitmap decodeFile(File f){
    try {
        //Decode image size
        BitmapFactory.Options o = new BitmapFactory.Options();
        o.inJustDecodeBounds = true;
        BitmapFactory.decodeStream(new FileInputStream(f),null,o);

        //The new size we want to scale to
        final int REQUIRED_SIZE=70;

        //Find the correct scale value. It should be the power of 2.
        int scale=1;
        while(o.outWidth/scale/2>=REQUIRED_SIZE && o.outHeight/scale/2>=REQUIRED_SIZE)
            scale*=2;

        //Decode with inSampleSize
        BitmapFactory.Options o2 = new BitmapFactory.Options();
        o2.inSampleSize=scale;
        return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
    } catch (FileNotFoundException e) {}
    return null;
}

611voto

AdamK Puntos 1629

El Android de Formación de clase, "la Visualización de mapas de bits de manera Eficiente", ofrece una gran información para comprender y tratar con la excepción java.lang.OutOfMemoryError: bitmap size exceeds VM budget cuando la carga de mapas de bits.

360voto

Thomas Vervest Puntos 1882

He hecho una pequeña mejora para Fedor del código. Básicamente hace lo mismo, pero sin el (en mi opinión) feo, mientras bucle y no siempre se traduce en una potencia de dos. Felicitaciones a Fedor para la fabricación de la solución original, me quedé hasta que me encontré con la suya, y entonces yo era capaz de hacer esto :)

 private Bitmap decodeFile(File f){
    Bitmap b = null;

        //Decode image size
    BitmapFactory.Options o = new BitmapFactory.Options();
    o.inJustDecodeBounds = true;

    FileInputStream fis = new FileInputStream(f);
    BitmapFactory.decodeStream(fis, null, o);
    fis.close();

    int scale = 1;
    if (o.outHeight > IMAGE_MAX_SIZE || o.outWidth > IMAGE_MAX_SIZE) {
        scale = (int)Math.pow(2, (int) Math.ceil(Math.log(IMAGE_MAX_SIZE / 
           (double) Math.max(o.outHeight, o.outWidth)) / Math.log(0.5)));
    }

    //Decode with inSampleSize
    BitmapFactory.Options o2 = new BitmapFactory.Options();
    o2.inSampleSize = scale;
    fis = new FileInputStream(f);
    b = BitmapFactory.decodeStream(fis, null, o2);
    fis.close();

    return b;
}

224voto

Ephraim Puntos 1661

Yo vengo de iOS experiencia y estaba frustrado al descubrir un patético problema con algo tan básico como la carga y mostrando una imagen. Después de todo, todo el mundo que tiene este problema es intentar mostrar un tamaño razonable imágenes. De todos modos, aquí están los dos cambios que se han resuelto mi problema (y de hecho mi aplicación muy sensible).

1) Cada vez que hagas BitmapFactory.decodeXYZ(), asegúrese de pasar en un BitmapFactory.Options con inPurgeable set a true (y, preferiblemente, con inInputShareable también se establece en true).

2) NUNCA use Bitmap.createBitmap(width, height, Config.ARGB_8888). Me refiero a que NUNCA! Yo nunca tuve ese algo que no se levante de la memoria de error después de algunos pases. Ninguna cantidad de recycle(), System.gc(), lo que ayudó. Es siempre la excepción. La otra manera en que realmente funciona es tener un maniquí de imagen en su dibujables (o de otro mapa de bits que se decodifica con el paso 1 de arriba), el reescalado que a lo que usted desea, luego manipular el mapa de bits resultante (como pasarlo a un Lienzo para obtener más divertido). Así, lo que debe utilizar en su lugar: Bitmap.createScaledBitmap(srcBitmap, width, height, false). Si por cualquier razón se DEBE utilizar la fuerza bruta método de crear, al menos pase Config.ARGB_4444.

Esto es casi garantizado el ahorro de horas, si no días. Todos los que hablan acerca de la escala de la imagen, etc. en realidad no funciona (a menos que considere la posibilidad de conseguir un tamaño incorrecto o imagen degradada de una solución).

Pero google, en serio?

72voto

Fraggle Puntos 2732

He tenido este mismo problema y lo soluciono, evitando la BitmapFactory.decodeStream o decodeFile funciones y en su lugar se utiliza BitmapFactory.decodeFileDescriptor

decodeFileDescriptor ve como se llama a diferentes métodos nativos de la decodeStream/decodeFile.

De todos modos, lo que funcionó fue esta (se nota que he añadido algunas opciones como el anterior, pero eso no es lo que hizo la diferencia. Lo que es crítico es el llamado a Bitmap.decodeFileDescriptor en lugar de decodeStream o decodeFile):

private void showImage(String path)   {
    Log.i("showImage","loading:"+path);
    BitmapFactory.Options bfOptions=new BitmapFactory.Options();
    bfOptions.inDither=false;                     //Disable Dithering mode
    bfOptions.inPurgeable=true;                   //Tell to gc that whether it needs free memory, the Bitmap can be cleared
    bfOptions.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
    bfOptions.inTempStorage=new byte[32 * 1024]; 


    File file=new File(path);
    FileInputStream fs=null;
    try {
        fs = new FileInputStream(file);
    } catch (FileNotFoundException e) {
        //TODO do something intelligent
        e.printStackTrace();
    }

    try {
        if(fs!=null) bm=BitmapFactory.decodeFileDescriptor(fs.getFD(), null, bfOptions);
    } catch (IOException e) {
        //TODO do something intelligent
        e.printStackTrace();
    } finally{ 
        if(fs!=null) {
            try {
                fs.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
    //bm=BitmapFactory.decodeFile(path, bfOptions); This one causes error: java.lang.OutOfMemoryError: bitmap size exceeds VM budget

    im.setImageBitmap(bm);
    //bm.recycle();
    bm=null;



}

Creo que hay un problema con el nativo de la función que se utiliza en decodeStream/decodeFile. Me han confirmado que un nativo método es llamado cuando el uso de decodeFileDescriptor. También lo que he leído es "que las Imágenes (mapas de bits) no se distribuye en un estándar de Java manera, pero a través de nativos de llamadas; las asignaciones se realizan fuera de la virtual montón, pero son contado contra ella!"

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X