Join FlipAndroid.COM Telegram Group: https://t.me/joinchat/F_aqThGkhwcLzmI49vKAiw


Java.lang.OutOfMemoryError: tamaño de mapa de bits supera el presupuesto de VM – Android

He desarrollado una aplicación que utiliza muchas imágenes en Android.

La aplicación se ejecuta una vez, llena la información en la pantalla ( Layouts , Textviews Listviews , Textviews , Textviews , etc) y el usuario lee la información.

No hay animación, ni efectos especiales ni nada que pueda llenar la memoria. A veces los tirantes pueden cambiar. Algunos son recursos de Android y algunos son archivos guardados en una carpeta en el SDCARD.

Entonces el usuario se cierra (el método onDestroy es ejecutado y la aplicación permanece en memoria por la VM) y luego en algún momento el usuario entra de nuevo.

Cada vez que el usuario entra en la aplicación, puedo ver la memoria cada vez más y más hasta que el usuario recibe el java.lang.OutOfMemoryError .

Entonces, ¿cuál es la mejor / correcta manera de manejar muchas imágenes?

¿Debería ponerlos en métodos estáticos para que no se carguen todo el tiempo? ¿Tengo que limpiar el diseño o las imágenes utilizadas en el diseño de una manera especial?

13 Solutions collect form web for “Java.lang.OutOfMemoryError: tamaño de mapa de bits supera el presupuesto de VM – Android”

Parece que tienes una pérdida de memoria. El problema no es manejar muchas imágenes, sino que sus imágenes no se están desasignando cuando se destruye su actividad.

Es difícil decir por qué esto es sin mirar su código. Sin embargo, este artículo tiene algunos consejos que podrían ayudar:

http://android-developers.blogspot.de/2009/01/avoiding-memory-leaks.html

En particular, el uso de variables estáticas es probable que empeore las cosas, no mejor. Es posible que deba agregar código que elimine las devoluciones de llamada cuando la aplicación se vuelva a dibujar – pero de nuevo, no hay suficiente información aquí para decir con seguridad.

Uno de los errores más comunes que encontré desarrollando aplicaciones de Android es el error "java.lang.OutOfMemoryError: tamaño de mapa de bits supera el presupuesto de VM". Encontré este error con frecuencia en actividades que utilizan muchos mapas de bits después de cambiar la orientación: la actividad se destruye, se crea de nuevo y los diseños se "inflan" del XML que consume la memoria de VM disponible para mapas de bits.

Los mapas de bits en el diseño de la actividad anterior no son correctamente des-asignados por el recolector de basura porque han cruzado referencias a su actividad. Después de muchos experimentos encontré una solución bastante buena para este problema.

Primero, establezca el atributo "id" en la vista principal de su diseño XML:

  <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:id="@+id/RootView" > ... 

A continuación, en el método onDestroy() de su actividad, llame al método unbindDrawables() pasando una referencia a la vista primaria y, a continuación, haga un System.gc() .

  @Override protected void onDestroy() { super.onDestroy(); unbindDrawables(findViewById(R.id.RootView)); System.gc(); } private void unbindDrawables(View view) { if (view.getBackground() != null) { view.getBackground().setCallback(null); } if (view instanceof ViewGroup) { for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) { unbindDrawables(((ViewGroup) view).getChildAt(i)); } ((ViewGroup) view).removeAllViews(); } } 

Este método unbindDrawables() explora el árbol de vista de forma recursiva y:

  1. Elimina las devoluciones de llamada en todos los elementos de fondo
  2. Elimina niños en cada grupo de vista

Para evitar este problema, puede utilizar el método nativo Bitmap.recycle() antes de null -ing objeto Bitmap (o establecer otro valor). Ejemplo:

 public final void setMyBitmap(Bitmap bitmap) { if (this.myBitmap != null) { this.myBitmap.recycle(); } this.myBitmap = bitmap; } 

Y a continuación puedes cambiar myBitmap sin llamar a System.gc() como:

 setMyBitmap(null); setMyBitmap(anotherBitmap); 

He encontrado este problema exacto. El montón es bastante pequeño por lo que estas imágenes pueden salir de control con bastante rapidez en lo que respecta a la memoria. Una forma es darle al recolector de basura una pista para recopilar memoria en un mapa de bits llamando a su método de reciclado .

Además, el método onDestroy no está garantizado para ser llamado. Es posible que desee mover esta lógica / limpiar en la actividad onPause. Consulte el diagrama / tabla de ciclo de vida de la actividad en esta página para obtener más información.

Esta explicación podría ayudar: http://code.google.com/p/android/issues/detail?id=8488#c80

Consejos rápidos:

1) NUNCA llame a System.gc () usted mismo. Esto se ha propagado como una corrección aquí, y no funciona. No lo hagas. Si usted notó en mi explicación, antes de obtener un OutOfMemoryError, la JVM ya ejecuta una recolección de basura por lo que no hay razón para hacer una nueva (su ralentización de su programa). Hacer uno al final de su actividad es sólo encubrir el problema. Puede causar que el mapa de bits se coloque en la cola del finalizador más rápidamente, pero no hay ninguna razón por la que no podría haber llamado simplemente reciclar en cada mapa de bits en su lugar.

2) Siempre llame a recycle () en mapas de bits que ya no necesita. Por lo menos, en la onDestroy de su actividad pasar y reciclar todos los mapas de bits que estaban utilizando. Además, si desea que las instancias de mapa de bits se recopilen de la pila dalvik más rápido, no hace daño para borrar las referencias al mapa de bits.

3) Llamar a recycle () y System.gc () todavía puede no quitar el mapa de bits de la pila de Dalvik. NO SE PREOCUPE de esto. Recycle () hizo su trabajo y liberó la memoria nativa, sólo tomará algún tiempo para pasar por los pasos que describí anteriormente para eliminar realmente el mapa de bits de la pila de Dalvik. Esto NO es un gran problema porque el gran trozo de memoria nativa ya es gratis!

4) Siempre asumir que hay un error en el último marco. Dalvik está haciendo exactamente lo que se supone que debe hacer. Puede que no sea lo que esperas o lo que quieres, pero es cómo funciona. "

Tuve el mismo problema. Después de algunas pruebas encontré que este error está apareciendo para grandes escalas de imagen. Reduje la escala de la imagen y el problema desapareció.

PS Al principio intenté reducir el tamaño de la imagen sin escalar la imagen. Eso no detuvo el error.

Los siguientes puntos realmente me ayudaron mucho. Puede haber otros puntos también, pero estos son muy importantes:

  1. Utilice el contexto de la aplicación (en lugar de activity.this) siempre que sea posible.
  2. Detener y liberar los hilos en el método de actividad onPause ()
  3. Libere sus vistas / devoluciones de llamada en el método de actividad onDestroy ()

Sugiero una manera conveniente de resolver este problema. Solo tiene que asignar el atributo "android: configChanges" como se indica en Mainfest.xml para su actividad con errores. Me gusta esto:

 <activity android:name=".main.MainActivity" android:label="mainActivity" android:configChanges="orientation|keyboardHidden|navigation"> </activity> 

La primera solución que había dado realmente había reducido la frecuencia de error de OOM a un nivel bajo. Pero, no resolvió el problema totalmente. Y luego daré la segunda solución:

Como el OOM detallada, he utilizado demasiada memoria de tiempo de ejecución. Por lo tanto, reducir el tamaño de la imagen en ~ / res / drawable de mi proyecto. Tal como una imagen sobrecalificada que tiene una resolución de 128X128, podría ser redimensionada a 64×64 que también sería adecuado para mi aplicación. Y después de hacerlo con un montón de imágenes, el error OOM no se produce de nuevo.

Yo también estoy frustrado por el bug de memoria. Y sí, también encontré que este error aparece mucho al escalar imágenes. Al principio intenté crear tamaños de imagen para todas las densidades, pero encontré que esto aumentó considerablemente el tamaño de mi aplicación. Así que ahora estoy usando una imagen para todas las densidades y escalando mis imágenes.

Mi aplicación lanzaría un error de memoria externa siempre que el usuario pasara de una actividad a otra. Configurar mi drawables a null y llamar a System.gc () no funcionó, tampoco reciclar mi bitmapDrawables con getBitMap (). Recycle (). Android seguiría lanzando el error outofmemory con el primer enfoque y lanzaría un mensaje de error de lienzo cada vez que intentara usar un bitmap reciclado con el segundo enfoque.

Tomé un enfoque incluso tercero. Fijé todas las vistas a null y el fondo al negro. Hago esta limpieza en mi método onStop (). Este es el método que se llama tan pronto como la actividad ya no es visible. Si lo hace en el método onPause (), los usuarios verán un fondo negro. No es ideal. En cuanto a hacerlo en el método onDestroy (), no hay garantía de que se llamará.

Para evitar que se produzca una pantalla en negro si el usuario presiona el botón Atrás del dispositivo, vuelvo a cargar la actividad en el método onRestart () llamando a los métodos startActivity (getIntent ()) y finish ().

Nota: en realidad no es necesario cambiar el fondo a negro.

Los métodos BitmapFactory.decode *, discutidos en la lección Load Big Bitmaps Efficiently , no se deben ejecutar en el subproceso principal de UI si los datos de origen se leen desde el disco o una ubicación de red (o cualquier fuente que no sea memoria). El tiempo que tardan estos datos en cargarse es impredecible y depende de una variedad de factores (velocidad de lectura del disco o de la red, tamaño de la imagen, potencia de la CPU, etc.). Si una de estas tareas bloquea el subproceso de UI, el sistema indica que su aplicación no responde y el usuario tiene la opción de cerrarla (consulte Diseño de la capacidad de respuesta para obtener más información).

Bueno, he intentado todo lo que encontré en Internet y ninguno de ellos funcionó. Llamar a System.gc () sólo arrastra la velocidad de la aplicación. Reciclar bitmaps en onDestroy no funcionó para mí también.

Lo único que funciona ahora es tener una lista estática de todo el mapa de bits para que los mapas de bits sobrevivan después de un reinicio. Y sólo utilizar los mapas de bits guardados en lugar de crear nuevos cada vez que la actividad si se reinicia.

En mi caso el código se ve así:

 private static BitmapDrawable currentBGDrawable; if (new File(uriString).exists()) { if (!uriString.equals(currentBGUri)) { freeBackground(); bg = BitmapFactory.decodeFile(uriString); currentBGUri = uriString; bgDrawable = new BitmapDrawable(bg); currentBGDrawable = bgDrawable; } else { bgDrawable = currentBGDrawable; } } 

Tuve el mismo problema sólo con cambiar las imágenes de fondo con tamaños razonables. Conseguí mejores resultados con fijar el ImageView al null antes de poner en una nueva imagen.

 ImageView ivBg = (ImageView) findViewById(R.id.main_backgroundImage); ivBg.setImageDrawable(null); ivBg.setImageDrawable(getResources().getDrawable(R.drawable.new_picture)); 

FWIW, he aquí un mapa de bits bitmap ligero que codifiqué y que he utilizado durante unos meses. No es todo-las-campanas-y-silbatos, así que lea el código antes de usarlo.

 /** * Lightweight cache for Bitmap objects. * * There is no thread-safety built into this class. * * Note: you may wish to create bitmaps using the application-context, rather than the activity-context. * I believe the activity-context has a reference to the Activity object. * So for as long as the bitmap exists, it will have an indirect link to the activity, * and prevent the garbaage collector from disposing the activity object, leading to memory leaks. */ public class BitmapCache { private Hashtable<String,ArrayList<Bitmap>> hashtable = new Hashtable<String, ArrayList<Bitmap>>(); private StringBuilder sb = new StringBuilder(); public BitmapCache() { } /** * A Bitmap with the given width and height will be returned. * It is removed from the cache. * * An attempt is made to return the correct config, but for unusual configs (as at 30may13) this might not happen. * * Note that thread-safety is the caller's responsibility. */ public Bitmap get(int width, int height, Bitmap.Config config) { String key = getKey(width, height, config); ArrayList<Bitmap> list = getList(key); int listSize = list.size(); if (listSize>0) { return list.remove(listSize-1); } else { try { return Bitmap.createBitmap(width, height, config); } catch (RuntimeException e) { // TODO: Test appendHockeyApp() works. App.appendHockeyApp("BitmapCache has "+hashtable.size()+":"+listSize+" request "+width+"x"+height); throw e ; } } } /** * Puts a Bitmap object into the cache. * * Note that thread-safety is the caller's responsibility. */ public void put(Bitmap bitmap) { if (bitmap==null) return ; String key = getKey(bitmap); ArrayList<Bitmap> list = getList(key); list.add(bitmap); } private ArrayList<Bitmap> getList(String key) { ArrayList<Bitmap> list = hashtable.get(key); if (list==null) { list = new ArrayList<Bitmap>(); hashtable.put(key, list); } return list; } private String getKey(Bitmap bitmap) { int width = bitmap.getWidth(); int height = bitmap.getHeight(); Config config = bitmap.getConfig(); return getKey(width, height, config); } private String getKey(int width, int height, Config config) { sb.setLength(0); sb.append(width); sb.append("x"); sb.append(height); sb.append(" "); switch (config) { case ALPHA_8: sb.append("ALPHA_8"); break; case ARGB_4444: sb.append("ARGB_4444"); break; case ARGB_8888: sb.append("ARGB_8888"); break; case RGB_565: sb.append("RGB_565"); break; default: sb.append("unknown"); break; } return sb.toString(); } } 
FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.