intive Argentina Blog

Memory leaks en Android Lollipop

Durante el desarrollo de una aplicación Android para una plataforma de diagnósticos clínicos, surgió un problema con Android Lollipop que no habíamos tenido con otras versiones. La app estaba basada y centrada en recursos estéticos, con muchas imágenes en cada activity (background, imagebuttons, shines, shadows) y variadas animaciones en las transiciones e interacción con el usuario. Su apk pesa actualmente 1,5 GB, más que nada, por el tamaño de videos que contiene, es una app pesada que hace que la experiencia del usuario, en cuanto al flow de la propia aplicación, dependa en buena medida del device que la corra.

En este ámbito, desarrollamos primero la aplicación para tablets de 10”, luego para 7” y, por último, para 9”. En el proceso, utilizamos el emulador Genymotion para testear el desarrollo en versiones Android 4.1 y 4.2, en mayor medida. Al comenzar la adaptación para 9” con un device virtual Genymotion GoogleNexus 9” – 5.0.0 (Lollipop), nos sorprendió que la app, luego de la transición de unas pocas pantallas, se detenía repentinamente y el debugger de Android Studio acusaba falta de memoria (OutOfMemory). ¡Y eso no había sucedido antes con la misma versión de la app! Por esto es que recomendamos probar las aplicaciones en varios emuladores para distintas versiones de Android, porque puede haber diferencia en el comportamiento de los componentes de una versión a otra.

Conscientes del peso de la aplicación, nos preguntábamos por qué no había ocurrido antes, cuando testeamos para 10” y 7”. Fue entonces cuando, después de pensar un rato y descartar otros posibles problemas, sospechamos de la versión de Android. Ahí fue cuando monitoreamos el uso de memoria durante la ejecución de la aplicación y notamos que la allocation de memoria aumentaba desde el inicio. Tenía bajas menores pero, en general, la tendencia era el aumento, sobre todo en la transición entre activities.

Investigando en la web, dimos con que la versión de Android Lollipop 5.0 tuvo desde su salida al mercado varios leaks de memoria. Entre ellos, uno muy grave, en el que se detenían el limpiado automático de la RAM y la deshabilitación de procesos para hacer uso del espacio, lo que resultaba en que las aplicaciones fueran terminadas o crashearan automáticamente, haciendo que los devices sean prácticamente inútiles.
Si bien se han solucionado varios problemas relacionados con el uso de la memoria en la versión 5.1, aún son varios los irresueltos.

En nuestro caso, probamos con Lollipop 5.1 y poco cambió: seguimos padeciendo problemas de Out Of Memory, incluso testeando con los distintos emuladores.

De todos modos, persistimos y continuamos investigando las posibles causas de esta falla de memoria. Finalmente, encontramos la solución al liberar a mano los recursos utilizados en las activities cada vez que estas son terminadas o pasadas a segundo plano:

Foto 1

Los crashes disminuyeron de forma drástica, pero aun así la app se pinchaba esporádicamente. Identificamos que lo hacía en las pantallas con múltiples páginas con un Pager.

Cuando entrábamos con Pagers dos o tres veces seguidas a estas activities, la memoria se incrementaba notoriamente. Había dos recursos importantes que no se estaban liberando en el código copiado arriba, por lo que decidimos hacer static los atributos FlipViewController y PageAdapter.

Foto 2

Foto 3

 

Bitmaps

Otro punto sobre el que hay que poner especial énfasis es el tratamiento de bitmaps, dado que este tipo de objetos son los que más memoria consumen. El mal uso de estos puede hacer que ocupemos rápidamente la memoria asignada por el sistema operativo y suframos el cierre repentino de nuestra aplicación.

En cuando a los bitmaps, les dejamos algunos tips a tener en cuenta:·

  • Guardar las imágenes con el tamaño con el que las vayamos a utilizar y no con el original. El tamaño de las imágenes en memoria ocupa de tres a cinco veces más espacio que el real.
  • Comprimir las imágenes no es lo mismo que guardarlas con el tamaño con que se utilizan.
  • Cachear las imágenes.
  • No adaptar la imagen al contenedor asignado del layout en el momento de renderear las pantallas es un factor importante para que la app corra en su velocidad óptima, lo que se traduce en una mejor experiencia para el usuario.
  • No tener recursos globales que no se liberen en toda la vida de la app.

 

Conclusión

No fue simple y debimos recorrer un camino de pruebas y errores para resolver el problema de memory leaks en esta app. La idea de plasmar ese recorrido en este artículo es que la experiencia vivida le sea útil a quien lo lea y demostrar que la perseverancia es un ingrediente fundamental a la hora de desarrollar. ¡Feliz codeo!

Fernando Cortés

Es colaborador externo de la Brigada Android. Es ingeniero en informática.

Deja un comentario