2010-07-13 5 views
20

Mi aplicación muestra una lista de 9 categorías y cada categoría muestra un flujo de cobertura basado en galería (gentilmente ofrecido por Neil Davies here) con imágenes de la categoría seleccionada.
Las imágenes se obtienen de la Web, cada una de las cuales tiene un tamaño de 300 K a 500 K, y se almacenan en una matriz de Dratables. Estos datos están vinculados al flujo de cobertura utilizando un BaseAdapter (código a continuación).
Cada vez que salgo del flujo de cobertura y vuelvo a la lista de categorías, borro el arrayList (de nuevo, el código a continuación).
En el escenario 1, mi arrayList contiene 5 Drawables. En este escenario, puedo navegar libremente por todas las categorías y mostrar sus imágenes. Durante mi prueba, hice un ciclo de 5 categorías en todas las categorías, lo que parece suficiente para determinar que no hay problema.
En el escenario 2, mi arrayList contiene 10 drawable. En este escenario, recibo una excepción OutOfMemoryError, mientras que ir a través de imágenes dentro de la categeory 5ª o 6ª:Android: excepción de falta de memoria en la Galería

 
07-13 08:38:21.266: ERROR/dalvikvm-heap(2133): 819840-byte external allocation too large for this process. 
07-13 08:38:21.266: ERROR/(2133): VM won't let us allocate 819840 bytes 
07-13 08:38:21.277: DEBUG/skia(2133): --- decoder->decode returned false 
07-13 08:38:21.287: WARN/dalvikvm(2133): threadid=25: thread exiting with uncaught exception (group=0x4001b188) 
07-13 08:38:21.296: ERROR/AndroidRuntime(2133): Uncaught handler: thread Thread-64 exiting due to uncaught exception 
07-13 08:38:21.308: ERROR/AndroidRuntime(2133): java.lang.OutOfMemoryError: bitmap size exceeds VM budget 
07-13 08:38:21.308: ERROR/AndroidRuntime(2133):  at android.graphics.BitmapFactory.nativeDecodeStream(Native Method) 
07-13 08:38:21.308: ERROR/AndroidRuntime(2133):  at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:459) 
07-13 08:38:21.308: ERROR/AndroidRuntime(2133):  at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:323) 
07-13 08:38:21.308: ERROR/AndroidRuntime(2133):  at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:697) 
07-13 08:38:21.308: ERROR/AndroidRuntime(2133):  at android.graphics.drawable.Drawable.createFromStream(Drawable.java:657) 

Esto no tiene sentido para mí. Si estoy perdiendo memoria, habría esperado colapsar en algún momento en el escenario 1, pero revisé todas las categorías una cantidad sustancial de veces y no choqué. También utilicé el complemento de Memory Analyzer para Eclipse que no presentó ningún culpable potencial.
Si el sistema no podía manejar 10 imágenes, como en scenarion 2, esperaba chocar en la primera categoría, pero choco solo después de 5 o 6 categorías.
algo de código:

funciones del adaptador de la CoverFlow:

public int getCount() { 
    return DataManager.getInstance().getImageBufferInstance().getImageArraySize(); 
} 

public Object getItem(int position) {  
    return DataManager.getInstance().getImagesBuffer().get(position); 
} 

public long getItemId(int position) { 
    return position; 
} 

public View getView(int position, View convertView, ViewGroup parent) {  
     ImageView i; 
     if (convertView == null) 
      i = new ImageView(mContext); 
     else 
      i = (ImageView)convertView; 
     Drawable bufferedImage = (Drawable)getItem(position); 
     Log.v("getView", "position: " + position); 
     i.setImageDrawable(bufferedImage); 

     i.setLayoutParams(new CoverFlow.LayoutParams(Utils.getInstance().getScreenWidth()/2, 
       Utils.getInstance().getScreenHeight()/2)); 
     i.setScaleType(ImageView.ScaleType.CENTER_INSIDE); 

     try{ 
     //Make sure we set anti-aliasing otherwise we get jaggies 
     BitmapDrawable drawable = (BitmapDrawable) i.getDrawable(); 
     drawable.setAntiAlias(true); 
     } 
     catch (Exception e) 
     { 
      Log.v("getView", "Exception: " + e.toString()); 
     } 
     return i;  
    } 

poblar la fuente de datos a la entrada a la categoría:

for (int i = 0; i < ImageBuffer.getInstance().getImageArraySize(); i++) 
{ 
    String imageUrl = ImageBuffer.getInstance().getImageUrl(i); 
    Log.v("Initial", imageUrl); 
    Drawable fullImage = AsyncImageLoader.getInstance().loadImageByUrl(imageUrl); 
    ImageBuffer.getInstance().getImages().add(i, fullImage); 

} 

limpieza de la fuente de datos al salir de la categoría (en acabado ()):

for (int i = 0; i < ImageBuffer.getInstance().getImageArraySize(); i++) 
{ 
    if (ImageBuffer.getInstance().images.get(i) != null) 
      { 
       ImageBuffer.getInstance().images.get(i).setCallback(null); 
       ImageBuffer.getInstance().images.set(i, null); 
      }  

} 

EDITAR:

Bien, apliqué la función LogHeap de Mathias en mi flujo de cobertura y aquí hay algunas salidas. Antes de cargar la primera galería:

 
DEBUG/Application(5221): debug. ================================= 
DEBUG/Application(5221): debug.heap native: allocated 6.20MB of 6.28MB (0.07MB free) in [com.example.Coverflow] 
DEBUG/Application(5221): debug.memory: allocated: 4.00MB of 24.00MB (0.00MB free) 
DEBUG/dalvikvm(5221): GC freed 4558 objects/638152 bytes in 84ms 
DEBUG/dalvikvm(5221): GC freed 17 objects/808 bytes in 67ms 

Después de entrar en la primera galería:

 
DEBUG/Application(5221): debug. ================================= 
DEBUG/Application(5221): debug.heap native: allocated 14.90MB of 16.89MB (0.07MB free) in [com.example.Coverflow] 
DEBUG/Application(5221): debug.memory: allocated: 4.00MB of 24.00MB (1.00MB free) 
DEBUG/dalvikvm(5221): GC freed 357 objects/50080 bytes in 68ms 
DEBUG/dalvikvm(5221): GC freed 353 objects/27312 bytes in 67ms 

Después existente la primera galería:

 
DEBUG/Application(5221): debug. ================================= 
DEBUG/Application(5221): debug.heap native: allocated 14.83MB of 16.89MB (0.11MB free) in [com.example.Coverflow] 
DEBUG/Application(5221): debug.memory: allocated: 4.00MB of 24.00MB (1.00MB free) 
DEBUG/dalvikvm(5221): GC freed 330 objects/17920 bytes in 77ms 
DEBUG/dalvikvm(5221): GC freed 13 objects/760 bytes in 67ms 

Después de entrar en la quinta galería:

 
DEBUG/Application(5221): debug. ================================= 
DEBUG/Application(5221): debug.heap native: allocated 16.80MB of 23.32MB (0.08MB free) in [com.example.Coverflow] 
DEBUG/Application(5221): debug.memory: allocated: 4.00MB of 24.00MB (1.00MB free) 
DEBUG/dalvikvm(5221): GC freed 842 objects/99256 bytes in 73ms 
DEBUG/dalvikvm(5221): GC freed 306 objects/24896 bytes in 69ms 

Después de salir de la quinta galería:

 
DEBUG/Application(5221): debug. ================================= 
DEBUG/Application(5221): debug.heap native: allocated 16.74MB of 23.32MB (0.11MB free) in [com.example.Coverlow] 
DEBUG/Application(5221): debug.memory: allocated: 4.00MB of 24.00MB (1.00MB free) 
DEBUG/dalvikvm(5221): GC freed 331 objects/18184 bytes in 68ms 
DEBUG/dalvikvm(5221): GC freed 60 objects/3128 bytes in 68ms 

Parece que cada vez se asigna más memoria al entrar en una galería, pero muy poco se libera después de salir. ¿No estoy limpiando mis dibujables correctamente? Para cada elemento de mi lista de mapeados, llamo a setCallBack (null) y establezco el elemento a nulo. ¿No es eso suficiente?
Desesperado por cualquier idea.
Gracias

+0

No necesita utilizar etiquetas HTML para los bloques de código. En su lugar, use lo que se proporciona con SO. El formato es muy bueno para ti. – JasCav

+0

Gracias Jason por la edición. Por algún motivo, no puedo mostrarlo correctamente utilizando la información proporcionada ... EDITAR: OK, ya lo entiendo. 4 espacios ... – Rob

Respuesta

38

Las imágenes se descargan desde la Web, cada que van desde 300K a 500K en tamaño, y se almacenan en un ArrayList de dibujables.

El tamaño del archivo kb de la imagen que está cargando desde la web no es directamente relevante. Dado que se convierten en mapas de bits, debe calcular el ancho * alto * 4 bytes por imagen para las imágenes ARGB normales. (ancho y alto en px).

Los mapas de bits consumen montón nativo, que generalmente no se muestra en un hprof. El hprof solo debe mostrarle la cantidad de objetos, es decir, BitmapDrawables o Bitmaps que quedan.

utilizo este código en mi aplicación a la salida de memoria utilizado actualmente utilizado por la aplicación y el montón orígenes:

public static void logHeap(Class clazz) { 
    Double allocated = new Double(Debug.getNativeHeapAllocatedSize())/new Double((1048576)); 
    Double available = new Double(Debug.getNativeHeapSize())/1048576.0); 
    Double free = new Double(Debug.getNativeHeapFreeSize())/1048576.0); 
    DecimalFormat df = new DecimalFormat(); 
    df.setMaximumFractionDigits(2); 
    df.setMinimumFractionDigits(2); 

    Log.d(APP, "debug. ================================="); 
    Log.d(APP, "debug.heap native: allocated " + df.format(allocated) + "MB of " + df.format(available) + "MB (" + df.format(free) + "MB free) in [" + clazz.getName().replaceAll("com.myapp.android.","") + "]"); 
    Log.d(APP, "debug.memory: allocated: " + df.format(new Double(Runtime.getRuntime().totalMemory()/1048576)) + "MB of " + df.format(new Double(Runtime.getRuntime().maxMemory()/1048576))+ "MB (" + df.format(new Double(Runtime.getRuntime().freeMemory()/1048576)) +"MB free)"); 
    System.gc(); 
    System.gc(); 

    // don't need to add the following lines, it's just an app specific handling in my app   
    if (allocated>=(new Double(Runtime.getRuntime().maxMemory())/new Double((1048576))-MEMORY_BUFFER_LIMIT_FOR_RESTART)) { 
     android.os.Process.killProcess(android.os.Process.myPid()); 
    } 
} 

que llamo al iniciar o terminar una actividad durante el desarrollo.

logHeap(this.getClass()); 

Aquí hay algunos enlaces informativos: generalmente hay muchos temas sobre este tema aquí.

Aquí es también una diapositiva útiles por Romain Guy (Android ingeniero marco) sobre las referencias suaves, referencias débiles, cachés simples, manejo de imagen: http://docs.huihoo.com/google/io/2009/Th_0230_TurboChargeYourUI-HowtomakeyourAndroidUIfastandefficient.pdf

+0

Mathias, muchas gracias. Voy a intentarlo – Rob

+0

Mathias, edité la pregunta con algunos resultados usando su función. ¿Qué piensa usted de esto? Gracias – Rob

+0

Después de examinar cuidadosamente la salida heapLog me di cuenta de que algo no se borra correctamente. Pensé que setCallBack (null) y anular el elemento sería suficiente, pero solo después de aplicar recycle() se borró la memoria. Gracias, Mathias. – Rob

0

La imagen que está cargando en la galería 5 o 6 puede ser demasiado grande para cargar y excede el tamaño máximo permitido por la máquina virtual.

+2

que suele ser de 16Mb. – Pentium10

+0

Hola Ryan. La galería 1 es de mayor tamaño y se carga. Además, no es una galería específica que se cuelga. Es la cantidad de galerías que ya pasé lo que importa. Por cierto, cada galería tiene entre 2,8 MB y 4,4 MB. – Rob

+0

No es necesariamente el tamaño de la galería, es el tamaño de la imagen, a menos que esté "precargando" todas las imágenes. –

4

Aquí son algunos consejos:

  1. ¿Utiliza la opción inSampleSize? Reduce el consumo de memoria si escalas imágenes. Strange out of memory issue while loading an image to a Bitmap object

  2. Debe llamar a Bitmap.recycle() cuando ya no necesite imágenes. Creo que es importante en tu caso. Android: OutofMemoryError: bitmap size exceeds VM budget with no reason I can see

+1

Gracias Fedor. Después de examinar la salida de la función logHeap de Mathias, me di cuenta de que algo no se está limpiando. Apliqué reciclar() y la memoria fue liberada. Con su permiso, le acreditaré a Mathias la respuesta. – Rob

+0

No hay problema :-)) – Fedor

+0

¡Gracias! SampleSize w00p w00p – Blundell

0

es mejor que ser muy conscientes de que la convertView en la lista de parámetros de getView es siempre null. Es decir, la galería no reutiliza la vista anterior en el interior.

Cuestiones relacionadas