¿Mi proceso de almacenamiento en caché de imágenes va a perder memoria?

Tengo un objeto que sobrescribe el objeto Application . En él, tengo una variable miembro que es un LongSparseArray donde la clave es algún identificador de tipo long y el valor es un objeto con 2 variables miembro: un Bitmap y un long que se utiliza como un timestamp.

Esta es mi caché de imágenes globales. Ocasionalmente, se ejecuta una función que mira las marcas de tiempo y edifica las cosas que tienen más de una hora de antigüedad.

Por "edad" quiero decir que elimina toda la entrada de la LongSparseArray .

Aquí está mi pregunta:

Supongamos que tengo una Activity con un ListView . Cada fila en el ListView tiene un ImageView que se rellena con una imagen de la memoria caché.

 Bitmap image = ((MyApp)getApplicationContext()).getImage(id); holder.imgImage.setImageBitmap(image); 

Ahora, supongamos que el usuario hace clic en un botón que los lleva a una nueva Activity . Mientras está en esta nueva Activity , la imagen asignada anteriormente a una fila en el ListView en las edades de Activity anteriores.

Por lo tanto, para recapitular, que Bitmap clave / entrada de valor ya no existe en el LongSparseArray global.

¿Es que Bitmap realmente puede ser recuperado por Java? ¿No es todavía referido por el ImageView en el ListView de la Activity anterior? Suponiendo, por supuesto, que Android no ha recuperado la memoria utilizada por esa Activity .

La razón por la que estoy preguntando acerca de esto es mi función de envejecimiento anterior también llamaría .Recycle() en el Bitmap . En este escenario, cuando el usuario pulsa el botón Atrás y devuelve a la Activity anterior que estaba utilizando ese Bitmap , la aplicación se bloquearía, presumiblemente porque ese Bitmap no sólo faltaba en la caché, sino también de memoria. Así que acabo de quitar la llamada .Recycle() .

Por cierto, una vez que el Bitmap se elimina de la caché, y un objeto con ese identificador aparece de nuevo en la pantalla, la aplicación descargará el Bitmap nuevo y colocarlo en la caché. Si el anterior se quedó en la memoria, se podría ver cómo esto presentaría un problema.

Además, ¿alguien tiene alguna idea para una solución más eficaz?

¿Qué pasaría si configuro myImageView.setDrawingCacheEnabled(false); ?

Hay 2 Activities que utilizan esta imagen de almacenamiento en caché. Una es una pantalla de búsqueda que muestra una lista de elementos (y sus imágenes) después de que el usuario realiza una búsqueda. La otra es una lista de los elementos que el usuario ha seleccionado para mantener.

Problema : Una vez que el método recycle () se llama en un mapa de bits, el mapa de bits nunca debe utilizarse de nuevo. Si se intenta dibujar el mapa de bits, se lanzará una excepción. De los documentos :

Debe utilizar recycle () sólo cuando esté seguro de que el bitmap ya no se está utilizando. Si llama a recycle () y más tarde intenta dibujar el mapa de bits, obtendrá el error: "Canvas: intentando utilizar un bitmap reciclado".

En este caso específico, ha reciclado el mapa de bits, pero ImageView del elemento ListView tiene una referencia fuerte al mapa de bits. Cuando vuelve a la actividad, el elemento ListView intenta dibujar el mapa de bits, por lo que se produce la excepción.

Gestión de memoria de mapa de bits : Antes de Android 2.3.3 , los datos de píxeles de respaldo de un mapa de bits se almacenaban en la memoria nativa y el propio mapa de bits en la memoria de Dalvik. Por lo tanto, para liberar la memoria nativa, el método de recycle tiene que ser llamado.

Aquí está Bitmap.recycle definición de la función:

  public void recycle() { if (!mRecycled) { if (nativeRecycle(mNativeBitmap)) { // return value indicates whether native pixel object was actually recycled. // false indicates that it is still in use at the native level and these // objects should not be collected now. They will be collected later when the // Bitmap itself is collected. mBuffer = null; mNinePatchChunk = null; } mRecycled = true; } } 

Post Android 3.0 , los datos de píxeles de respaldo también se almacenan en la memoria de Dalvik. Cuando el mapa de bits ya no es necesario, debemos asegurarnos de que no mantenemos ninguna referencia fuerte al mapa de bits, de modo que se recoja la basura.

Solución : Si sigue soportando Android 2.3.3 y versión inferior, todavía necesita utilizar recycle para liberar el mapa de bits.

Puede utilizar el recuento de referencia para realizar un seguimiento si el mapa de bits está siendo referenciado actualmente por el elemento ListView, de modo que incluso se envejece, no se llama recycle en el mapa de bits.

El método getView de ListView adapater es el lugar donde se asigna el mapa de bits al ImageView. Aquí se incrementa el recuento de referencias. Puede adjuntar setRecyclerListener a ListView para saber cuándo se coloca el elemento listview en la papelera de reciclaje. Éste es el lugar en el que disminuiría el recuento de referencia del mapa de bits. La función de envejecimiento necesita reciclar el mapa de bits sólo si el recuento de referencia es cero.

También puede considerar el uso de LruCache para el almacenamiento en caché, como se menciona en documentos .

SetDrawingCacheEnabled : Al llamar a este método con parámetro true, la siguiente llamada a getDrawingCache dibujará la vista a un mapa de bits. La versión de mapa de bits de la vista se puede representar en la pantalla. Dado que es sólo un mapa de bits, no podemos interactuar con él como se hace con una vista real. Un par de casos de uso son:

  • Cuando ListView se desplaza, el mapa de bits de la vista de elementos mostrados se captura y se procesa. Para que las vistas que se despliegan no se someten a medida y el diseño pasa.
  • Ver la función de jerarquía en DDMS.

¿Es que Bitmap realmente puede ser recuperado por Java? ¿No es todavía referido por el ImageView en el ListView de la actividad anterior? Suponiendo, por supuesto, que Android no ha recuperado la memoria utilizada por esa actividad.

El Bitmap se detiene en el ListView (una referencia fuerte) para que dalvik no pueda recuperar su memoria.

Al parecer, no se puede llamar a recycle en el mapa de bits o cosas malas sucederá (caída de la aplicación, por ejemplo).

¿Qué pasaría si establezco myImageView.setDrawingCacheEnabled (false) ;?

Si desactivas el caché de dibujo, cada vez que tu vista necesite ser redibujada, se onDraw método onDraw. No estoy muy familiarizado con ImageView , puedes ir y leer su fuente para una comprensión profunda. (Nota: el uso de la caché de dibujo es diferente cuando accerleration de hardware está habilitado / deshabilitado, aquí asumo que está usando renderizado de software).

Para la solución, puede probar lo siguiente:

  1. Cuando el caché de Bitmap se vuelve obsoleto, lo eliminas de la matriz de caché (y entonces la aplicación intentará obtener una nueva, creo).
  2. En ListView.getView , puede comprobar si las edades Bitmap utilizadas actualmente. Debe ser fácil porque usted sabe la marca de tiempo cuando usted llama a setImageBitmap la primera vez y el timestamp más último. Si no son iguales, usted llama setImageBitmap otra vez usando el nuevo mapa de bits y el viejo será reclamado.

Deseo que esto ayude.

En cuanto a, "¿Alguien tiene alguna idea para una solución más efectiva?"

La biblioteca de Picasso ayudaría a resolver los problemas a los que se enfrentan http://square.github.io/picasso/

Picasso es "una poderosa descarga de imágenes y biblioteca de almacenamiento en caché para Android"

"Muchos peligros comunes de carga de imágenes en Android son manejados automáticamente por Picasso:

  • Manejo del recobro de ImageView y cancelación de la descarga en un adaptador.
  • Memoria automática y caché de disco. "
  • Android RatingBar se pinta mal
  • Mostrando un botón de eliminación en el desplazamiento de un listview para Android
  • ¿Por qué getView devuelve objetos convertView incorrectos en SeparatedListAdapter?
  • showDialog en el botón Listview adapter
  • Android: casillas de verificación en un ListView (comportamiento extraño del elemento seleccionado)
  • Listview se bloquea en modo retrato cuando se hace clic en elementos (con fragmentos)
  • Crash después de agregar pie de página a ListView
  • Android set listview height dinámicamente
  • Android: agrega un elemento a la lista personalizada al pulsar el botón
  • Cómo analizar el valor en el atributo en el análisis xml
  • Resaltar el elemento seleccionado en ListView en Android
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.