Android: ¿Cómo funciona el reciclaje de mapas de bits ()?

89

Digamos que he cargado una imagen en un objeto de mapa de bits como

Bitmap myBitmap = BitmapFactory.decodeFile(myFile);

Ahora, ¿qué pasará si cargo otro mapa de bits como

myBitmap = BitmapFactory.decodeFile(myFile2);

¿Qué pasa con el primer myBitmap? ¿Se recolecta la basura o tengo que recolectarla manualmente antes de cargar otro mapa de bits? myBitmap.recycle()?

Además, ¿existe una mejor manera de cargar imágenes grandes y mostrarlas una tras otra mientras se reciclan en el camino?

Anuj Tenani
fuente

Respuestas:

23

Creo que el problema es el siguiente: en las versiones anteriores a Honeycomb de Android, los datos de mapa de bits sin procesar no se almacenan en la memoria de la máquina virtual sino en la memoria nativa. Esta memoria nativa se libera cuando el Bitmapobjeto java correspondiente es GC'd.

Sin embargo , cuando se queda sin memoria nativa, el dalvik GC no se activa, por lo que es posible que su aplicación use muy poca memoria de Java, por lo que el dalvik GC nunca se invoca, pero usa toneladas de memoria nativa para mapas de bits. que eventualmente causa un error OOM.

Al menos esa es mi suposición. Afortunadamente, en Honeycomb y versiones posteriores, todos los datos de mapas de bits se almacenan en la VM, por lo que no debería tener que usarlos recycle()en absoluto. Pero para los millones de 2.3 usuarios (la fragmentación sacude el puño ), debe usar recycle()siempre que sea posible (una molestia enorme). O, alternativamente, puede invocar el GC en su lugar.

Timmmm
fuente
21

Deberá llamar a myBitmap.recycle () antes de cargar la siguiente imagen.

Dependiendo de la fuente de su myFile (por ejemplo, si es algo que no tiene control sobre el tamaño original), al cargar una imagen en lugar de simplemente volver a muestrear un número arbitrario, debe escalar la imagen al tamaño de visualización.

if (myBitmap != null) {
    myBitmap.recycle();
    myBitmap = null;
}
Bitmap original = BitmapFactory.decodeFile(myFile);
myBitmap = Bitmap.createScaledBitmap(original, displayWidth, displayHeight, true);
if (original != myBitmap)
    original.recycle();
original = null;

Guardo en caché displayWidth y displayHeight en una estática que inicialicé al comienzo de mi actividad.

Display display = getWindowManager().getDefaultDisplay();
displayWidth = display.getWidth();
displayHeight = display.getHeight();
djunod
fuente
3
No es necesario que llames a recycling (), es una buena idea si quieres liberar la memoria de inmediato.
Karu
13
La respuesta aceptada dice "Si desea liberar memoria lo antes posible, debe llamar a reciclar ()". Su respuesta dice "Deberá llamar a myBitmap.recycle ()". Hay una diferencia entre "debería" y "necesita", y esta última es incorrecta en este caso.
Karu
1
El contexto es importante. La pregunta era "También hay una mejor manera de cargar imágenes grandes y mostrarlas una tras otra reciclándolas en el camino".
djunod
3
A partir de Android 4.1, el ejemplo anterior puede fallar porque createScaledBitmap puede devolver en algunos casos la misma instancia que la original. Eso significa que debe verificar ese original! = MyBitmap antes de reciclar el original.
Jeremyfa
1
@Jeremyfa Solo devuelve la imagen original si especifica un ancho y alto que es idéntico al original. En ese caso, el escalado es discutible, por lo que también podría salvar algunos procesos saltándolo y devolviendo la imagen original. Sin embargo, no debería "romper" nada ...
Jabari
11

Una vez que el mapa de bits se había cargado en la memoria, de hecho se hizo con datos de dos partes. La primera parte incluye información sobre el mapa de bits, la otra parte incluye información sobre los píxeles del mapa de bits (está compuesto por una matriz de bytes). La primera parte existe en la memoria usada de Java, la segunda parte existe en la memoria usada de C ++. Puede usar la memoria del otro directamente. Bitmap.recycle () se utiliza para liberar la memoria de C ++. Si solo hace eso, el GC recopilará la parte de java y la memoria de C siempre se usará.

Allen
fuente
+1 para una forma interesante pero muy buena de describir por qué la memoria no está disponible para GC inmediato: buena.
Richard Le Mesurier