Unlimited Plugins, WordPress themes, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. Android

Detectando y Resolviendo Problemas de Performance en Android

by
Difficulty:IntermediateLength:LongLanguages:

Spanish (Español) translation by Nadia Castelli (you can also view the original English article)

Introducción

No importa cuán innovadora y útil sea tu aplicación Android, si es lenta, propensa a "colgarse", o usa excesiva memoria, nadie querrá usarla.

La performance es muy importante, pero además es algo de lo cual es fácil olvidarte cuando estás ocupado añadiendo los toques finales a tu hermosa interfaz de usuario o ideando nuevas y emocionantes funcionalidades para tu aplicación.

Esto sucede hasta que las calificaciones negativas comienzan a aparecer en Google Play.

En este artículo, recibirás un curso intensivo acerca de los problemas de performance más comunes a los que cada desarrollador Android debe estar atento. Aprenderás a probar si estos problemas están ocurriendo en tus propios proyectos, utilizando las herramientas provistas por el Android SDK—más una herramienta que ya está instalada en tu dispositivo Android.

Si descubres un problema de performance en tu aplicación, obviamente querrás solucionarlo. Entonces, a lo largo del recorrido también aprenderás a usar las herramientas del Android SDK para recolectar más información acerca de los problemas de performance que puedan estar pasando desapercibidos. Una vez obtenida esta información, tendrás mejor entendimiento de cómo perfeccionar la performance de tu aplicación, y por ende crear una aplicación que la gente ame usar.

1. Superposición

Paso 1: El Problema

La interfaz de usuario de tu aplicación es tu conexión con el usuario, pero crear algo que se vea bonito es solamente la mitad de la batalla. También debes asegurarte de que tu interfaz de usuario se renderize rápidamente y corra de manera fluida.

Una de las causas más comunes de una interfaz de usuario lenta y poco responsiva es la superposición (overdraw). El overdraw ocurre cuando desperdicias tiempo de procesamiento de GPU coloreando píxeles que luego son coloreados por algo más.

Por ejemplo, imagina un fondo azul con texto encima. Android no solamente pinta de azul las áreas que son visibles para el usuario, pinta el fondo azul por completo y luego dibuja el texto encima. Esto significa que algunos píxeles son coloreados dos veces, lo que conlleva superposición.

Un poco de superposición, como en el ejemplo anterior, es inevitable. Demasiada, sin embargo, puede tener un impacto notable en el rendimiento de tu aplicación, por lo que querrás minimizarla cuando sea posible.

Chequear tu aplicación en busca de superposición es relativamente sencillo. Demasiada superposición puede indicar un problema subyacente en tu interfaz de usuario, como vistas redundantes—más tarde veremos esto en profundidad. Por estas razones, cuando estés buscando problemas de performance en tu aplicación, la superposición es una parte sensible por la cual empezar.

Paso 2: Detectando la Superposición

La buena noticia es que tu dispositivo Android ya trae incluida una herramienta que te permite chequear la cantidad de superposición en cualquier aplicación instalada en tu dispositivo.

Como esta funcionalidad vive en tu dispositivo, el primer paso es instalar la aplicación que quieres testear en tu dispositivo Android. Luego, para comprobar la cantidad de superposición, simplemente debes abrir la Configuración de tu dispositivo, seleccionar las opciones de Programador, y tocar Depurar superposición con GPU seguido de Mostrar áreas superpuestas.

On your device select Settings Developer Options Debug GPU Overdraw Show overdraw area

Esta herramienta utiliza bloques de color para resaltar las diferentes cantidades de superposición. Lo único que resta por hacer es abrir la aplicación que deseas testear y revisar cuál es la situación con respecto a la superposición.

Launch your app and check the amount of overdraw
  • Incoloro: Sin superposición. Esto significa que el píxel sólo fue coloreado una vez.
  • Azul: Una única superposición (1x). El píxel fue pintado dos veces.
  • Verde: Superposición de 2x. Este píxel fue coloreado tres veces. Como regla general, deberías apuntar a una superposición máxima de 2x.
  • Rojo claro: Superposición de 3x. Dependiendo de tu aplicación, algunas pequeñas áreas de rojo claro pueden ser inevitables, pero si ves zonas medianas o grandes de dicho color deberías investigar qué está causando tanta superposición.
  • Rojo oscuro: Superposición de 4x. Este píxel fue coloreado cinco o más veces. Definitivamente querrás descubrir qué está causando cualquier área de color rojo oscuro.

Paso 3: Minimizando la Superposición

Una vez identificada una zona de superposición significativa, la forma más sencilla de reducirla es abrir los archivos XML de tu aplicación y buscar las zonas de superposición, particularmente cualquier dibujable que no sea visible al usuario, y cualquier fondo que esté siendo renderizado por sobre otro.

También deberías buscar áreas donde el atributo background esté coloreado de blanco cuando un padre también está pintado del mismo color. Todas estas cosas pueden causar superposición significativa.

El sistema Android puede reducir casos simples de superposición automáticamente, pero vale la pena notar que esto no se extiende a vistas personalizadas complejas donde Android no tiene visión de cómo se está dibujando el contenido.

Si estás utilizando vistas personalizadas complejas en tu aplicación, puedes definir límtes para los dibujables de tus vistas utilizando el método clipRect. Para más información, recomiendo visitar la documentación oficial de Android.

2. Tubería de Renderizado de Android

Paso 1: El Problema

Otra causa común de problemas de performance, es la jerarquía de vistas de tu aplicación. Para renderizar cada vista, Android pasa por tres etapas:

  1. medición
  2. disposición
  3. dibujo

El tiempo que toma a Android completar estas etapas es proporcional al número de vistas en tu jerarquía. Esto significa que una de las formas más sencillas de reducir el tiempo de renderizado de tu aplicación es identificar y remover cualquier vista que no esté contribuyendo a la imagen final que el usuario ve en su dispositivo.

Aún si todas las vistas de la jerarquía son necesarias, la forma en que están acomodadas puede tener un impacto significativo en la fase de medición del proceso de renderizado. Generalmente hablando, cuanto más profunda tu jerarquía de vistas, más tardará en completar la fase de medición.

Durante el proceso de renderizado, cada vista provee sus dimensiones a la vista madre. Si esta última descubre algún inconveniente con cualquiera de esas dimensiones, puede forzar a cada vista hija a volver a medirse.

Las re-mediciones pueden ocurrir incluso cuando no existe error alguno. Por ejemplo, los relative layouts suelen tener que medir a sus hijos dos veces para que todo quepa en pantalla. Los linear layouts con hijos que utilizan el parámetro layout_weight rutinariamente miden a cada hijo dos veces.

Dependiendo de cómo estén ordenadas tus vistas, las mediciones y re-mediciones pueden ser un proceso costoso y consumidor de tiempo que tiene un impacto notable en la velocidad de renderizado de tu aplicación.

La clave para asegurar que tu interfaz de usuario se renderiza rápida y suavemente, es remover toda vista innecesaria y buscar oportunidades para aplanar tu maquetado.

El Android SDK incluye una herramienta, el Visor de Jerarquía, que permite visualizar la jerarquía de vistas entera. Esta herramienta ayuda a descubrir vistas redundantes y layouts anidados.

Paso 2: Usando el Visor de Jerarquía

Antes de mirar más de cerca a la herramienta Visor de Jerarquía, existen ciertos caprichos que debes conocer. Primero, el Visor de Jerarquía sólo puede comunicarse con una aplicación que está corriendo, no con el código fuente de tu aplicación. Esto significa que necesitarás instalar la aplicación que quieres testear en tu dispositivo Android, o usar un emulador.

Hay otro detalle mayor además. Por defecto, el Visor de Jerarquía sólo puede comunicarse con un dispositivo que esté corriendo una versión de desarrollo del sistema operativo Android. Si no tienes un dispositivo de desarollador, puedes sortear esta restricción agregando la clase ViewServer a tu aplicación.

Una vez que estés listo para usar el Visor de Jerarquía, abre Android Studio y selecciona Tools de la barra de herramientas, seguido de Android y Android Device Monitor.

Select Tools Android Android Device Monitor

Clickea el botón de Hierarchy View a la derecha, como se muestra en la siguiente imagen.

Select the Hierarchy View button from the toolbar

Sobre el lado izquierdo de la pantalla hay una pestaña Windows que lista todos los dispositivos y emuladores Android detectados. Selecciona tu dispositivo y verás una lista de los procesos que están corriendo en el mismo. Selecciona el proceso al que quieres revisar más de cerca, y tres áreas del Visor de Jerarquía se actualizarán automáticamente.

Estas tres ventanas proveen tres representaciones visuales diferentes de la jerarquía de vistas:

  • Tree View: Una vista aérea de tu jerarquía de vistas, donde cada nodo representa una única vista.
  • Tree Overview: Una representación en mapa de tu jerarquía de vistas. Esta vista es particularmente útil para identificar oportunidades de aplanar tu maquetado.
  • Layout View: Una representación en bloques de tu jerarquía de vistas.

Estas tres ventanas están vinculadas. Si seleccionas una vista en una ventana, figurará resaltada en las otras dos. Puedes usar las tres ventanas simultáneamente para buscar vistas redundantes escondidas en la jerarquía.

Select a view in one window and itll appear highlighted in the other two

Si no estás seguro de si una vista está contribuyendo realmente en algo a la imagen final, simplemente dirígete a Tree View y clickea el nodo en cuestión. Verás una vista previa de cómo esta vista se mostrará en pantalla, y podrás ver exactamente cómo contribuye la misma a tu aplicación.

Pero sólo porque una vista está contribuyendo en algo a la imagen finalmente renderizada no significa que además no esté contribuyendo a un gran problema de performance. Ya has visto cómo podemos usar el Visor de Jerarquía para encontrar layouts anidados de manera obvia, ¿pero qué pasa si este anidamiento destructor de performance no es tan obvio? ¿O qué sucede si hay algo más causando que una vista se renderice lentamente?

La buena noticia es que además puedes usar el Visor de Jerarquía para perfilar cuánto tiempo le toma a cada vista la transición entre las diferentes fases del proceso de renderizado. Esto te brinda una forma de descubrir problemas de renderizado que pueden no ser muy obvios a primera vista.

La siguiente sección muestra cómo utilizar el Visor de Jerarquía para perfilar las diferentes vistas de tu maquetado en busca de problemas de renderizado que puedan permanecer ocultas bajo la superficie.

Paso 3: Perfilado Por Nodos

La manera más fácil de identificar cuellos de botella en tu interfaz de usuario es recolectando datos acerca de cuánto tiempo toma a cada una de tus vistas completar las etapas de medición, disposición y dibujo del proceso de renderizado.

No sólo puedes emplear el Visor de Jerarquía para recolectar esta información, este también muestra estos datos de manera muy visual y sencilla de comprender, para que puedas identificar de un vistazo cuáles son las vistas que no tienen buen rendimiento.

El Visor de Jerarquía no muestra los tiempos de renderizado por defecto. Puedes agregar esta información yendo al Tree View y seleccionando el nodo raíz de la parte del árbol que deseas testear. Luego, invoca la función de perfilado del Visor de Jerarquía clickeando el ícono de diagrama de Venn verde, rojo, y violeta, como se muestra en la imagen siguiente.

 To invoke Hierarchy Viewers profiling feature click the green red and purple Venn diagram icon

Tres puntos coloreados aparecerán en cada nodo en esta parte de la jerarquía. De izquierda a derecha, estos puntos representan:

  • el tiempo que toma medir la vista
  • el tiempo que toma disponer la vista
  • el tiempo que toma dibujar la vista

Cada punto tiene además un color asignado:

  • Verde: Para esta porción del tiempo de renderizado, esta vista es más veloz que al menos la mitad de los nodos perfilados. Por ejemplo, un punto verde en la posición de Layout significa que esta vista tiene un tiempo de disposición menor que al menos el 50% de los nodos perfilados.
  • Amarillo: Para esta porción del tiempo de renderizado, esta vista cae dentro del 50% más lento de todos los nodos perfilados.
  • Rojo: Para esta porción del tiempo de renderizado, esta vista es la más lenta de todos los nodos perfilados.

Luego de recabar estos datos, no sólo sabrás qué vistas necesitas optimizar, sino también exactamente qué parte del proceso de renderizado está causando que esa vista se renderice más lentamente.

Debes tener en cuenta que aunque las vistas con puntos amarillos y rojos pueden ser el lugar lógico donde enfocar tus esfuerzos por optimizar la aplicación, estos indicadores de performance son relativos a los otros nodos perfilados en la jerarquía de vistas. En otras palabras, siempre tendrás algunas vistas que se rendericen más lentamente que otras.

Antes de que empieces a limpiar tu código para optimizar una vista en particular, pregúntate a tí mismo si esta vista posee una buena razón para renderizarse más lentamente que los otros nodos perfilados, o si realmente es esta una oportunidad para disminuir la velocidad de renderizado de tu aplicación.

3. Fugas de Memoria

Paso 1: El Problema

Aunque Android es un entorno con gestión de memoria, no dejes que esto te de una falsa sensación de seguridad—las fugas de memoria aún pueden ocurrir. Esto es porque el garbage collector (GC, o recolector de basura) sólo puede remover objetos que reconozca como inalcanzables. Si no detecta un objeto inalcanzable, luego ese objeto no será tenido en cuenta por el garbage collector.

Estos objetos inalcanzables pululan por ahí, contaminando tu pila y ocupando espacio valioso. A medida que tu aplicación continúa filtrando objetos, la cantidad de espacio utilizable disminuye cada vez más, lo que a su vez dispara eventos del GC más frecuentes y largos.

Estas son malas noticias por dos motivos. Primero, mientras que los eventos del GC normalmente no tienen un impacto notable sobre la performance de tu aplicación, muchos eventos del GC en un intervalo corto de tiempo pueden resultar en una interfaz de usuario lenta y poco responsiva. El segundo problema es que los dispositivos móviles tienden a tener poca memoria en un principio, así que una fuga de memoria puede escalar rápidamente hacia un OutOfMemoryError, ocasionando que tu aplicación se detenga.

Las fugas de memoria pueden ser difíciles de detectar. Puede que sólo te des cuenta de que la memoria representa un problema para tu aplicación cuando tus usuarios comiencen a quejarse. Afortunadamente, el Android SDK posee algunas herramientas útiles que puedes usar para buscar esas frecuentemente pequeñas señales de fuga de memoria en tu aplicación.

Paso 2: Monitor de Memoria

El Monitor de Memoria es una manera sencilla de obtener un vistazo general del uso de memoria de tu aplicación a lo largo del tiempo. Debes notar que esta herramienta puede conectarse solamente a una aplicación que esté corriendo, así que asegúrate de que la aplicación que deseas testear está instalada en tu dispositivo Android, y que el mismo está conectado a tu computadora.

Esta herramienta está incluida en Android Studio, y se accede clickeando la pestaña Memory ubicada en la parte inferior del IDE. Tan pronto como el Monitor de Memoria detecte la aplicación funcionando, comenzará a grabar el uso de memoria de la misma.

Memory Monitor is built into Android Studio you can access it by clicking the Memory tab

Si el Monitor de Memoria no comienza a grabar, corrobora que tu dispositivo esté seleccionado en el menú de dispositivos.

Si el Monitor de Memoria retorna el mensaje No debuggable applications, abre el menú Tools de Android Studio, selecciona Android, y asegúrate de que Enable adb integration esté seleccionado. Esta funcionalidad puede ser un poco temperamental, por lo cual tal vez debas encender y apagar Enable adb integration un par de veces. También puede ser de ayuda remover tu dispositivo Android y luego volver a conectarlo.

Una vez que el Monitor de Memoria ha detectado tu aplicación funcionando, mostrará la cantidad de memoria que la misma está utilizando en color azul oscuro, y la memoria no asignada en azul claro.

Invierte algo de tiempo interactuando con tu dispositivo mientras observas cómo cambia el uso de memoria de tu aplicación en el Monitor de Memoria. Eventualmente, la memoria asignada crecerá hasta que no quede más memoria libre. En este punto, el sistema liberará memoria disparando un evento del GC. Cuando notes un descenso significativo en la memoria asignada, es una señal de que ha ocurrido un evento del GC.

Los eventos del GC son perfectamente normales, pero debes procuparte si notas que tu aplicación asigna mucha memoria en un período corto de tiempo, o que los eventos del GC son demasiado frecuentes. Estas son dos señales reveladoras de que existe una fuga de memoria en tu aplicación.

Si utilizas el Monitor de Memoria para tracear una fuga de memoria sospechada a lo largo de un período de tiempo significativo, tal vez notes que el sistema Android intenta compensar la creciente demanda de memoria de tu aplicación garantizándole un techo de memoria mayor, punto en el cual el ciclo comienza de nuevo.

Eventualmente, verás que tu aplicación consume tanta memoria que el sistema no puede poner más memoria a su disposición. Si ves que esto sucede, algo serio está pasando con la forma en que tu aplicación utiliza la memoria.

Paso 3: Monitor de Dispositivos Android

Otra herramienta que puede ayudarte a recabar más información sobre fugas de memoria y otros problemas relacionados con la memoria es la pestaña Heap del Monitor de Dispositivos Android.

La pestaña Heap puede ayudarte a diagnosticar fugas de memoria, mostrando cuánta memoria le ha asignado el sistema a tu aplicación. Como mencioné anteriormente, si la memoria asignada continúa aumentando, es un fuerte indicador de que tu aplicación posee una fuga de memoria.

Pero esta herramienta brinda además muchísimos datos acerca del uso de la pila por parte de tu aplicación, incluso el tipo de objetos que está asignando, el número de objetos asignados, y cuánto espacio están ocupando los mismos. Esta información extra puede ser invaluable cuando estás investigando la fuente de fugas de memoria y otros problemas relacionados con la memoria en tu aplicación.

Para acceder a esta herramienta, abre el Monitor de Dispositivos Android y selecciona la pestaña DDMS. En el panel Devices, selecciona tu dispositivo y el proceso que deseas examinar. Luego, selecciona la pestaña Heap, como se muestra en la imagen siguiente, y emplea algo de tiempo interactuando con tu aplicación.

Launch Android Debug Monitor select DDMS and then click the Heap tab

La salida de la pila sólo se muestra luego de un evento del GC, así que para poblar esta pestaña con datos tendrás que esperar que un evento del GC ocurra naturalmente, o puedes forzar uno clickeando el botón Cause GC.

Una vez ocurrido un evento del GC, la pestaña Heap se actualizará con un montón de información acerca del uso que hace tu aplicación de la pila. Estos datos se refrescarán luego de cada evento del GC.

Conclusión

En este tutorial, hemos cubierto algunos de los problemas de performance más comunes que debes tener en cuenta al desarrollar applicaciones Android: superposición, fugas de memoria, y renderizado de UI lento.

Además has comenzado a involucrarte con algunas de las herramientas que puedes utilizar para comprobar si estos problemas están ocurriendo en tus propios proyectos Android, y has visto cómo recolectar más información acerca de los posibles problemas de performance que ocurran en tus propias aplicaciones. Cuanto más información tengas, mejores chances tienes de descubrir la causa del problema y solucionarlo.

El Android SDK posee muchas más herramientas que pueden ayudarte a diagnosticar y atacar problemas de rendimiento. Si quieres aprender más, las páginas oficiales de documentación de Android sobre Traceview and dmtracedump y Allocation Tracker contienen más información.

¡Sé el primero en conocer las nuevas traducciones–sigue @tutsplus_es en Twitter!

Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.