Aplicación de Android de problemas de memoria – probado todo y aún en una pérdida

Pasé 4 días completos tratando todo lo posible para averiguar la pérdida de memoria en una aplicación que estoy desarrollando, pero las cosas dejaron de tener sentido hace mucho tiempo.

La aplicación que estoy desarrollando es de naturaleza social, así que piensa en el perfil Actividades (P) y en la lista Actividades con datos – por ejemplo, insignias (B). Puede pasar de un perfil a una lista de distintivos a otros perfiles, a otras listas, etc.

Así que imagina un flujo como este P1 -> B1 -> P2 -> B2 -> P3 -> B3, etc Por coherencia, estoy cargando perfiles e insignias del mismo usuario, por lo que cada página P es el mismo y así es Cada página B.

La idea general del problema es: después de navegar por un poco, dependiendo del tamaño de cada página, obtengo una excepción de falta de memoria en lugares aleatorios – Bitmaps, Strings, etc – no parece ser coherente.

Después de hacer todo lo imaginable para averiguar por qué me estoy quedando sin memoria, he llegado a nada. Lo que no entiendo es por qué Android no está matando P1, B1, etc, si se queda sin memoria al cargar y en lugar de bloqueos. Esperaría que estas actividades anteriores para morir y ser resucitado si alguna vez Volver a ellos a través de onCreate () y onRestoreInstanceState ().

Y mucho menos esto – incluso si lo hago P1 -> B1 -> Back -> B1 -> Back -> B1, todavía tengo un accidente. Esto indica algún tipo de pérdida de memoria, pero incluso después de descargar hprof y usar MAT y JProfiler, no puedo identificarlo.

He inhabilitado la carga de imágenes de la web (y aumentado los datos de prueba cargados para compensarlo y hacer que la prueba sea justa) y me aseguré de que la caché de imágenes use SoftReferences. Android realmente trata de liberar los pocos SoftReferences que tiene, pero justo antes de que se bloquea de memoria.

Las páginas de la insignia obtienen datos de la web, se cargan en una matriz de EntityData desde un BaseAdapter y se alimentan a ListView (en realidad estoy usando el excelente MergeAdapter de CommonsWare , pero en esta actividad Badge, en realidad sólo hay 1 adaptador de todos modos, pero Quería mencionar este hecho de cualquier manera).

He pasado por el código y no fue capaz de encontrar nada que se filtra. Limpié y anulado todo lo que pude encontrar e incluso System.gc () a la izquierda y la derecha, pero la aplicación se bloquea.

Todavía no entiendo por qué las actividades inactivas que están en la pila no se obtienen, y me encantaría averiguar eso.

En este punto, estoy buscando cualquier sugerencias, consejos, soluciones … cualquier cosa que pueda ayudar.

Gracias.

Todavía no entiendo por qué las actividades inactivas que están en la pila no se obtienen, y me encantaría averiguarlo.

No es así como funcionan las cosas. La única gestión de memoria que impacta el ciclo de vida de la actividad es la memoria global en todos los procesos, ya que Android decide que se está agotando la memoria y por lo tanto necesita matar los procesos de fondo para recuperar algo.

Si su aplicación se está sentando en el primero plano que comienza más y más actividades, nunca va en el fondo, así que golpeará siempre su límite local de la memoria del proceso antes de que el sistema llegue siempre cerca de matar su proceso. (Y cuando destruye su proceso, matará el proceso que aloja todas las actividades, incluyendo lo que esté actualmente en primer plano).

Por lo tanto, me parece que su problema básico es: usted está dejando que muchas actividades se ejecuten al mismo tiempo, y / o cada una de esas actividades se aferra a demasiados recursos.

Sólo tiene que rediseñar su navegación para no confiar en apilar un número arbitrario de actividades potencialmente pesadas. A menos que hagas una cantidad seria de cosas en onStop () (como llamar a setContentView () para borrar la jerarquía de la vista de la actividad y borrar las variables de cualquier otra cosa que pueda estar reteniendo), solo te quedarás sin memoria.

Es posible que desee considerar el uso de las nuevas API de fragmentos para reemplazar esta pila arbitraria de actividades con una sola actividad que más estrictamente administra su memoria. Por ejemplo, si utiliza las instalaciones de la pila trasera de fragmentos, cuando un fragmento pasa a la pila trasera y ya no es visible, su método onDestroyView () se llama para eliminar completamente su jerarquía de vista, reduciendo considerablemente su huella.

Ahora, en la medida en que se estrelle en el flujo donde se presiona hacia atrás, ir a una actividad, presionar hacia atrás, ir a otra actividad, etc y nunca tienen una pila profunda, entonces sí, sólo tienes una fuga. Esta entrada de blog describe cómo depurar fugas: http://android-developers.blogspot.com/2011/03/memory-analysis-for-android.html

Algunos consejos:

  1. Asegúrese de que no es un contexto de actividad de fugas.

  2. Asegúrese de que no se mantienen las referencias en los mapas de bits. Limpie todos los ImageView's en Activity # onStop, algo como esto:

     Drawable d = imageView.getDrawable(); if (d != null) d.setCallback(null); imageView.setImageDrawable(null); imageView.setBackgroundDrawable(null); 
  3. Recicle los mapas de bits si ya no los necesita.

  4. Si utiliza caché de memoria, como memory-lru, asegúrese de que no está usando mucha memoria.

  5. No sólo las imágenes toman mucha memoria, asegúrese de no mantener demasiados otros datos en la memoria. Esto fácilmente puede suceder si usted tiene listas infinitas en su aplicación. Intente almacenar datos en caché en DataBase.

  6. En android 4.2, hay un error (stackoverflow # 13754876) con aceleración de hardware, por lo que si utiliza hardwareAccelerated=true en su manifiesto se perderá la memoria. GLES20DisplayList – mantener la celebración de referencias, incluso si usted hizo el paso (2) y nadie más está haciendo referencia a este mapa de bits. Aquí usted necesita:

    A) deshabilitar la aceleración de hardware para api 16/17;
    o
    B) separar la vista que sostiene el mapa de bits

  7. Para Android 3+ puedes intentar usar android:largeHeap="true" en tu AndroidManifest . Pero no solucionará tus problemas de memoria, solo posponerlos.

  8. Si necesita, como, la navegación infinita, entonces Fragmentos – debe ser su elección. Así que tendrás 1 actividad, que solo cambiará entre fragmentos. De esta manera también resolverá algunos problemas de memoria, como el número 4.

  9. Utilice el Analizador de memoria para averiguar la causa de su pérdida de memoria.
    Aquí hay un video muy bueno de Google I / O 2011: Administración de memoria para aplicaciones de Android
    Si se trata de mapas de bits esto debe ser un debe leer: Visualización de mapas de bits de manera eficiente

Los mapas de bits son a menudo el culpable de errores de memoria en Android, por lo que sería una buena zona para comprobar.

¿Tiene referencias a cada actividad? AFAIK esta es una razón que evita que Android elimine actividades de la pila.

¿Eres capaz de reproducir este error en otros dispositivos también? He experimentado un comportamiento extraño de algunos dispositivos Android dependiendo de la ROM y / o el fabricante del hardware.

Creo que el problema tal vez una combinación de muchos factores que se indican aquí en las respuestas son lo que está dando problemas. Al igual que @Tim dijo, una referencia (estática) a una actividad o un elemento en esa actividad puede hacer que el GC omita la Actividad. Aquí está el artículo que discute esta faceta. Yo creo que el probable problema proviene de algo que mantiene la Actividad en un estado de "Proceso Visible" o superior, lo que garantizará que la Actividad y sus recursos asociados nunca sean reclamados.

Pasé por el problema opuesto hace un tiempo con un servicio, así que eso es lo que me hizo ir en este pensamiento: hay algo que mantiene su actividad en la lista de prioridad de proceso para que no esté sujeto al sistema GC, como Una referencia (@Tim) o un bucle (@Alvaro). El bucle no necesita ser un elemento interminable o de ejecución larga, algo que se ejecuta mucho como un método recursivo o un bucle en cascada (o algo así como esas líneas).

EDIT: Como entiendo esto, onPause y onStop son llamados automáticamente por Android. Los métodos están allí principalmente para que usted overide para que pueda cuidar de lo que necesita antes de que el proceso de alojamiento se detiene (guardar variables, guardar manualmente estado, etc); Pero tenga en cuenta que se indica claramente que onStop (junto con onDestroy) no se puede llamar en todos los casos. Además, si el proceso de hosting también está alojando una actividad, servicio, etc. que tiene un estado "Forground" o "Visible", el sistema operativo podría ni siquiera mirar detener el proceso / hilo. Por ejemplo: una actividad y un servicio están en el mismo proceso y el servicio devuelve START_STICKY de onStartCommand() el proceso toma automáticamente al menos un estado visible. Ésa podría ser la clave aquí, intenta declarar un nuevo proc para la Actividad y ver si cambia algo. Trate de añadir esta línea a la declaración de su actividad en el manifiesto como: android:process=":proc2" y luego ejecutar las pruebas de nuevo si su actividad comparte un proceso con cualquier otra cosa. El pensamiento aquí es que si has limpiado tu actividad y estás bastante seguro de que el problema no es tu actividad, entonces algo más es el problema y su tiempo de cazador para eso.

Además, no recuerdo dónde lo vi (si incluso lo vi en los documentos de Android), pero recuerdo que algo acerca de un PendingIntent referencia a una actividad puede provocar que una actividad se comporte de esta manera.

Aquí hay un enlace para la página onStartCommand() con algunas ideas sobre el frente de no matar el proceso.

Así que lo único en lo que realmente puedo pensar es si tienes una variable estática que hace referencia directa o indirectamente al contexto. Incluso algo así como una referencia a parte de la aplicación. Estoy seguro de que ya lo ha probado, pero lo voy a sugerir por si acaso, trate de anular todas las variables estáticas en la onDestroy () sólo para asegurarse de que el recolector de basura lo recibe

La mayor fuente de pérdida de memoria que he encontrado fue causada por alguna referencia global, de alto nivel o de larga data al contexto. Si mantiene "contexto" almacenado en una variable en cualquier lugar, puede encontrar fugas de memoria impredecibles.

Intente pasar getApplicationContext () a cualquier cosa que necesite un contexto. Es posible que tenga una variable global que contenga una referencia a sus Actividades y que evite que se recojan basura.

Una de las cosas que realmente ayudó a la cuestión de la memoria en mi caso terminó siendo establecer inPurgeable a true para mis Bitmaps. Consulte ¿Por qué no utilizaría la opción inPurgeable de BitmapFactory? Y la discusión de la respuesta para más información.

La respuesta de Dianne Hackborn y nuestra discusión subsiguiente (también gracias, CommonsWare) ayudó a aclarar ciertas cosas de las que estaba confundido, así que gracias por eso.

He encontrado el mismo problema contigo. Yo estaba trabajando en una aplicación de mensajería instantánea, para el mismo contacto, es posible iniciar una ProfileActivity en una ChatActivity, y viceversa. Acabo de añadir una cadena adicional en la intención de iniciar otra actividad, que toma la información de tipo de clase de la actividad de arranque, y el ID de usuario. Por ejemplo, ProfileActivity inicia una ChatActivity, luego en ChatActivity.onCreate, marca el tipo de clase invocador 'ProfileActivity' y el id de usuario, si va a iniciar una Actividad, comprobaría si es una 'ProfileActivity' para el usuario o no . Si es así, simplemente llame a 'finish ()' y vuelva a la antigua ProfileActivity en lugar de crear una nueva. La pérdida de memoria es otra cosa.

  • SplashScreen con la imagen PNG conduce a Android.Views.InflateException seguido de OutOfMemory
  • OutOfMemoryError: Al recibir una respuesta XML de 2.3 MB
  • Android Drawable vs Asset en rendimiento de carga de imágenes
  • Android claro hilo webview, libre de memoria, evitar OutOfMemoryError
  • Error de falta de memoria al cargar más imágenes en Glide
  • Gran cantidad de memoria no recogida de basura
  • Crear una pantalla de bienvenida animada con marcos en Android
  • ¿Cómo evitar OutOfMemory para TextView que contiene ilimitado ImageSpans?
  • Evite la pérdida de memoria de Android cuando no utilice Imagen estática
  • Android ViewPager / PagerAdapter ImageView OutOfMemoryError
  • ¿Cómo saber cuánto tamaño de montón libre está disponible para bitmap para Android 2. *?
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.