2012-08-22 12 views
6

Como dice el título, tengo una excepción de falta de memoria al cargar diseños. Uno pensaría que esto es un problema de pérdida de memoria, pero después de lucharlo durante 2 días ya no estoy tan seguro. La aplicación tiene más de 10 actividades, la mayoría de las cuales tienen imágenes de fondo.OutOfMemoryError crear mapas de bits

pocos hechos/descubrimientos que hicieron:

  • problema hasta ahora sólo aparece en el Galaxy Nexus con Android 4.0.3. No pude reproducirlo en Nexus S (4.1.1) y Galaxy S II (2.3.3).

  • la orientación de la pantalla no cambia. De hecho, la mayoría de mis actividades se enfocaron en el retrato de todos modos.

  • solo para risas Agregué la llamada al finish() al abrir una nueva actividad, por lo que no habría más de una actividad en la memoria en ese momento. Verificado que se llama onDestroy.

define como:

@Override 
public void onDestroy() 
{ 
    super.onDestroy(); 
    cleanupDrawables(contentView); 

    // null all the fields referencing views and drawables 

    System.gc(); 
} 

donde cleanupDrawables() es:

protected static void cleanupDrawables(View view) 
{ 
    cleanupDrawable(view.getBackground()); 

    if (view instanceof ImageView) 
     cleanupDrawable(((ImageView)view).getDrawable()); 
    else if (view instanceof TextView) 
    { 
     TextView tv = (TextView)view; 
     Drawable[] compounds = tv.getCompoundDrawables(); 
     for (int i = 0; i < compounds.length; i++) 
      cleanupDrawable(compounds[i]); 
    } 
    else if (view instanceof ViewGroup && !(view instanceof AdapterView)) 
    { 
     ViewGroup vg = (ViewGroup)view; 
     for (int i = 0; i < vg.getChildCount(); i++) 
      cleanupDrawables(vg.getChildAt(i)); 
     vg.removeAllViews(); 
    } 
} 

protected static void cleanupDrawable(Drawable d) 
{ 
    if (d == null) 
     return; 

    d.setCallback(null); 

    if (d instanceof BitmapDrawable) 
     ((BitmapDrawable)d).getBitmap().recycle(); 
    else if (d instanceof LayerDrawable) 
    { 
     LayerDrawable layers = (LayerDrawable)d; 
     for (int i = 0; i < layers.getNumberOfLayers(); i++) 
      cleanupDrawable(layers.getDrawable(i)); 
    } 
} 
  • mirando el inspector montón Eclipse, la memoria parece ser estable, es decir, algunas actividades tienen más memoria que otros, pero se libera al cierre, y parece estable en el tiempo.

  • acuerdo con respuestas relacionadas con lo que las imágenes se almacenan en la memoria nativa, pero this tio afirma que debe ser puesto en el montón Android 3, por lo que debe ver a aumentar la memoria si esto era verdad pérdida de memoria de imagen.

El resultado final de mi esfuerzo es que aún me sale un error de memoria, aunque no tan rápido como antes. Antes de que ocurra el error, empiezo a ver corrupción de mapa de bits visible, que no era el caso antes de agregar el código cleanupDrawables(). Deduje que llamar a Bitmap.recycle() está causando daños, aunque este código solo se invoca en onDestroy. La corrupción aparece en ambos mapas de bits que son parte de los estilos comunes que aparecen en muchas actividades, así como los mapas de bits que solo se muestran en una actividad.

En resumen, los resultados de mi investigación no son concluyentes. En este punto, no sé qué más probar.

El seguimiento de la pila de error de referencia:

08-22 10:49:51.889: E/AndroidRuntime(31697): java.lang.RuntimeException: Unable to start activity ComponentInfo{klick.beatbleeds/klick.beatbleeds.Bleeds}: android.view.InflateException: Binary XML file line #67: Error inflating class <unknown> 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1955) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1980) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.app.ActivityThread.access$600(ActivityThread.java:122) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1146) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.os.Handler.dispatchMessage(Handler.java:99) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.os.Looper.loop(Looper.java:137) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.app.ActivityThread.main(ActivityThread.java:4340) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at java.lang.reflect.Method.invokeNative(Native Method) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at java.lang.reflect.Method.invoke(Method.java:511) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at dalvik.system.NativeStart.main(Native Method) 
08-22 10:49:51.889: E/AndroidRuntime(31697): Caused by: android.view.InflateException: Binary XML file line #67: Error inflating class <unknown> 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.view.LayoutInflater.createView(LayoutInflater.java:606) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at com.android.internal.policy.impl.PhoneLayoutInflater.onCreateView(PhoneLayoutInflater.java:56) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.view.LayoutInflater.onCreateView(LayoutInflater.java:653) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:678) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.view.LayoutInflater.rInflate(LayoutInflater.java:739) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.view.LayoutInflater.inflate(LayoutInflater.java:489) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.view.LayoutInflater.inflate(LayoutInflater.java:396) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.view.LayoutInflater.inflate(LayoutInflater.java:352) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at klick.beatbleeds.ActivityBase.setContentView(ActivityBase.java:82) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at klick.beatbleeds.Bleeds.onCreate(Bleeds.java:40) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.app.Activity.performCreate(Activity.java:4465) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1049) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1919) 
08-22 10:49:51.889: E/AndroidRuntime(31697): ... 11 more 
08-22 10:49:51.889: E/AndroidRuntime(31697): Caused by: java.lang.reflect.InvocationTargetException 
08-22 10:49:51.889: E/AndroidRuntime(31697): at java.lang.reflect.Constructor.constructNative(Native Method) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at java.lang.reflect.Constructor.newInstance(Constructor.java:417) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.view.LayoutInflater.createView(LayoutInflater.java:586) 
08-22 10:49:51.889: E/AndroidRuntime(31697): ... 23 more 
08-22 10:49:51.889: E/AndroidRuntime(31697): Caused by: java.lang.OutOfMemoryError 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.graphics.Bitmap.nativeCreate(Native Method) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.graphics.Bitmap.createBitmap(Bitmap.java:605) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.graphics.Bitmap.createBitmap(Bitmap.java:551) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.graphics.Bitmap.createScaledBitmap(Bitmap.java:437) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.graphics.BitmapFactory.finishDecode(BitmapFactory.java:524) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:499) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:351) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:773) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.content.res.Resources.loadDrawable(Resources.java:1937) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.content.res.TypedArray.getDrawable(TypedArray.java:601) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.view.View.<init>(View.java:2780) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.view.ViewGroup.<init>(ViewGroup.java:385) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.widget.LinearLayout.<init>(LinearLayout.java:174) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.widget.LinearLayout.<init>(LinearLayout.java:170) 
08-22 10:49:51.889: E/AndroidRuntime(31697): ... 26 more 

Aquí está una captura de pantalla de una de las vistas a obtener la idea de la cantidad de imágenes se utilizan aquí enter image description here

+0

Entonces, ¿qué tan grandes son estos mapas de bits (resolución)? Si los reemplaza con imágenes más pequeñas (solo por patadas), ¿desaparecen los problemas? – dmon

+0

El recurso de mapa de bits de limpieza de la vista no es necesario y solo ralentizará su transición a una nueva actividad. ¿Qué tipo de diseño está inflando y con cuántas imágenes? – DeeV

+0

También estoy de acuerdo con @Deev, no debería necesitar "limpiar" manualmente sus bitmaps en destroy. – dmon

Respuesta

2

utilizar estos métodos estáticos para encontrar el problema exacto (s):

public static void showBitmapSize(Bitmap bitmap) { 
    Log.d("test", "bitmap dimensions: w:" + bitmap.getWidth() + ", h:" + bitmap.getHeight() + " memory: " + (bitmap.getRowBytes() * bitmap.getHeight()/1048576d)); 
} 

Y lo más importante:

static double lastavail; 
static double initavail; 
static boolean first = true; 

public static void showMemoryStats() { 
    showMemoryStats(""); 
} 

public static void showMemoryStats(String message) { 
    Log.i("memory", message + "----------------------------------------------------------------------------------------"); 
    double nativeUsage = Debug.getNativeHeapAllocatedSize(); 
    Log.i("memory", "nativeUsage: " + (nativeUsage/1048576d)); 
    //current heap size 
    double heapSize = Runtime.getRuntime().totalMemory(); 
//  Log.i("memory", "heapSize: " + (heapSize/1048576d)); 
    //amount available in heap 
    double heapRemaining = Runtime.getRuntime().freeMemory(); 
//  Log.i("memory", "heapRemaining: " + (heapRemaining/1048576d)); 
    double memoryAvailable = Runtime.getRuntime().maxMemory() - (heapSize - heapRemaining) - nativeUsage; 
    Log.i("memory", "memoryAvailable: " + (memoryAvailable/1048576d)); 

    if (first) { 
     initavail = memoryAvailable; 
     first = false; 
    } 
    if (lastavail > 0) { 
     Log.i("memory", "consumed since last: " + ((lastavail - memoryAvailable)/1048576d)); 
    } 
    Log.i("memory", "consumed total: " + ((initavail - memoryAvailable)/1048576d)); 

    lastavail = memoryAvailable; 

    Log.i("memory", "-----------------------------------------------------------------------------------------------"); 
} 

Dividir por 1048576 es solo para obtener valores en MB (al menos para mí es más fácil pensar en MB).

Llamar al showMemoryStats con algún mensaje significativo antes de la llamada setContentView(), y otra después. Y cuando comiences una actividad nueva, etc. Al final, conocerás los motivos exactos de tu problema.

Reciclaje manual puede ser necesario. Tuve que implementarlo en algunos lugares de mi aplicación. También usaba un mapa de bits intensivo (muchos fondos y fotos) y tenía este tipo de problemas en todos los dispositivos. Con estos métodos, pude encontrar todos los problemas y manejarlos apropiadamente.

Ah. Y existe una posible solución rápida para su problema, usted dice que solo aparece en Galaxy Nexus. Es el único dispositivo xhdpi de los que mencionas. Es probable que tenga todos los mapas de bits solo en la carpeta dibujable o drawable-hdpi. El dispositivo xhdpi tomará los mapas de bits de drawable o drawable-hdpi y escalará (aunque pueden estar ya en el tamaño correcto), y esto consumirá mucha memoria. Solución: cree una carpeta drawable-xhdpi, si no existe, y coloque una copia de los bitmaps allí.

Cuestiones relacionadas