Herramientas para profiling y linting
Introducción
El perfilamiento (profiling) consiste en la investigación del comportamiento de un software específico por medio del análisis de información recopilada. El linting consiste en utilizar herramientas que permitan identificar código sospechoso dentro de un proyecto de cualquier lenguaje. Generalmente el linting ocurre sobre código estático, se buscan casos en los cuales exista código muerto o resultados fuera de rango.
Objetivos
Utilizar herramientas para análisis de información generada por la aplicación.
Realizar análisis sobre distintos criterios como RAM, uso de la red, Heap, ubicación en memoria.
Optimizar aplicaciones por medio de herramientas no-oficiales como New Relic.
Enunciado
Para esta guía se pretende mostrar los distintos comportamientos esperados en las herramientas de profiling. Se pretende añadir un punto crítico a la aplicación. Luego de agregar el punto se presentarán los resultados al llegar a este punto por medio de varias estadísticas y métricas de información del dispositivo.
Enlaces Recomendados
Allocation tracker: https://developer.android.com/studio/profile/am-allocation.html
Hprof: https://developer.android.com/studio/profile/am-hprof.html
Hotspot y New Relic
En la primera sección se pretende inyectar un hot-spot dentro de la aplicación que induce un cierre de la aplicación. Para no interrumpir completamente el funcionamiento y poder realizar las pruebas correctamente se creará donde antes se realizaba el pedido.
Para lograr parar la aplicación se inyectará manualmente un error. En la actividad PedidosActivity se debe editar el método realizarPedido. En este método se inyectará un retraso temporal medio de un for y se hará un llamado recursivo al método para poder llenar el stack del dispositivo. La siguiente imagen refleja el resultado esperado:

Para probar el correcto funcionamiento del hotspot se debe ejecutar la aplicación desde el IDE. Al presionar el botón de enviar pedido, de la vista de pedidos de un plato, deben pasar unos segundos hasta que se cierre la aplicación. Otro comportamiento que se debe observar es una fluctuación en las medidas de Android Monitor. Este, se debe abrir en una de las pestañas inferiores luego de ejecutar la aplicación. Al hacer click en el botón se ve un crecimiento inmediato en el monitor de memoria y mayor actividad de CPU, cuando se cierra la aplicación simplemente se apagan todos los medidores de manera automática hasta que se vuelve a ejecutar la aplicación. La imagen que se presenta fue tomada justo antes de que se pierda la información de los sensores debido a el cierre de la aplicación.

Adicionalmente a las herramientas oficiales que vienen con el SDK o embebidas en el IDE, existen herramientas externas para soportar el análisis de problemas de desempeño, por ejemplo, Relic. Esta herramienta requiere de una configuración del servicio externo e instrumentar el código de la app para poder guardar guardar datos de análisis en tiempo de ejecución. Lo primero que se debe hacer es acceder a la página de New Relic.

Al entrar al hipervínculo se accede a una página de bienvenida, en esta se explica de manera detallada todos los beneficios que ofrece la herramienta para la plataforma Android y se presentan de manera resumida los más importantes.
New Relic ofrece todo tipo de herramientas para optimizar el código del proyecto. Desde código estático que frena la velocidad de la aplicación hasta errores que no se habían presentado con simples pruebas de usuario. Una de las funcionalidades más importantes es el servicio de alerta de errores. Normalmente sin Firebase ni herramientas externas, la única forma de encontrar errores críticos es acceder a la consola de Play Store, en la que ni si quiera se presentan todos los casos de cierre de la aplicación. Las alertas de New Relic se envían de manera automática al dueño del proyecto justo después del momento en que ocurren.
El siguiente paso consiste en oprimir el botón de Trial, esta acción llevará a una nueva página en la que se deben introducir los datos del usuario para la creación de la cuenta.

Al terminar el proceso de registro la página redirige de manera automática a la pestaña de plataformas móviles de la página. En esta página se presenta una pequeña guía de configuración de la plataforma y se crea el nuevo proyecto en el que se guardarán los datos de la aplicación.

Luego del registro, el primer paso consiste en seleccionar la plataforma inicial en la que se debe configurar el proyecto de new Relic. En este caso se debe seleccionar Android.

Luego de seleccionar la plataforma se debe ingresar el nombre del proyecto que guardará la información. Al dar click en continuar se presentan los siguientes pasos para conectar la herramienta con la aplicación desarrollada. Primero se debe acceder al archivo build.gradle del proyecto general y se deben agregar tanto el repositorio como el classpath para acceder a la librería. El resultado debe ser el siguiente:

Al terminar de agregar la información al gradle del proyecto se debe incluir la librería de la herramienta dentro de las dependencias que están en el archivo build.gradle del app. Al final del archivo se deben agregar dos líneas para aplicar el plugin de newrelic, com.android.application y newrelic.

Teniendo configurado gradle se deben agregar dos permisos al archivo AndroidManifest de la aplicación.

Los dos permisos son INTERNET y ACCESS_NETWORK_STATE.
<user-permission android:name="android.permission.INTERNET"/></center>
<user-permission android:name="android.permission.ACCESS_NETWORK_STATE"/></center>
También se debe hacer un cambio dentro del archivo MainActivity.java de la aplicación. Dentro de éste se debe agregar la siguiente línea de importación:
import com.newrelic.agent.android.NewRelic;
Esto se necesita para poder hacer uso del objeto NewRelic y configurarlo para la aplicación.
Dentro del método onCreate de esta misma actividad se debe agregar la línea de configuración de la herramienta. Para configurar correctamente se debe llamar el método .withApplicationToken el cual recibe por parámetro el token generado por New Relic en el proyecto creado anteriormente.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mSwipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.activity_main_swipe_refresh_layout);
listView=(ListView)findViewById(R.id.platos_listview);
gpsText=(TextView)findViewById(R.id.gps_textview);
databaseHelper = new PlatosDatabase(this);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Intent i = new Intent(MainActivity.this,PedidosActivity.class);
i.putExtra("name", ((Plato)platos.get(position)).getNombre());
i.putExtra("id", ((Plato)platos.get(position)).getId());
startActivity(i);
}
});
mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
getPlatos();
}
});
mSwipeRefreshLayout.setRefreshing(true);
getPlatos();
receiver = new GPSReceiver(gpsText);
this.registerReceiver(receiver, new IntentFilter(GPS_FILTER));
Intent intent = new Intent(this, GPSIntentService.class);
intent.setAction(GPSIntentService.GETPOSITION);
this.startService(intent);
fab = (FloatingActionButton)findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
addPlatoDialogo();
}
});
NewRelic.withApplicationToken(
"AA790d665057cfa6e1e12d5556e47fc497aabd0e1d"
).start(this.getApplication());
}
Al terminar de realizar estos pasos de configuración se debe probar la aplicación, un poco de interacción y movimiento entre las vistas ayuda a la herramienta a conseguir más información. Luego de cierto tiempo de pruebas y de pasar un par de veces por el hotspot se debe obtener un resultado como el siguiente en el dashboard de la aplicación creada.

Los 4 indicadores clave son la cantidad de cierres (crashes), errores de red, tiempo de respuesta http y cantidad de ejecuciones de la aplicación. Debido a la naturaleza de esta guía no se presentan datos de solicitudes HTTP.
En esta misma página debajo de los 4 indicadores se presenta una tabla con las interacciones frecuentes de los usuarios. Esta información permite saber cual de todas las utilidades de la aplicación es la que más utilizan los usuarios o en la que más interes presentan. En esta tabla se presenta información detallada, desde la cantidad de veces que se ha llegado a esta vista hasta el promedio de la duración promedio de los usuarios en la vista.

Dentro de New Relic se encuentra una barra vertical que presenta las distintas utilidades que puede encontrar el usuario.

Las dos principales pestañas que se presentan en esta guía son el análisis de cierres (crashes) y las interacciones del proyecto.
Al entrar a la opción de interacciones se presenta un indicador más detallado que el de la pantalla principal. Se presentan todas las interacciones con las vistas y se muestra la siguiente gráfica que contiene los horarios y las visitas con mayor pico de interacción de los usuarios.

También se presenta el tiempo promedio que duran los usuarios en cada vista y en que hora especificamente ocurrio esto.

Al entrar a la vista de errores de cierre se presenta una tabla con la cantidad de veces que ha ocurrido cada tipo de crash.

Se presenta una gráfica con el top 5 de ubicaciones que muestra los cierres de la aplicación.

Uno de los detalles más importantes es que esta vista muestra la línea exacta del error e inclusive presenta el Stack Trace del error que ocurre; está información normalmente se pierde incluso en la consola de Android Studio debido a que se cierra automáticamente la aplicación.

Allocation Tracker
Allocation Tracker se utiliza para descubrir como ocurrió la alocación de elementos en memoria durante cierto periodo de tiempo. Para poder tomar esta información se debe realizar una captura de la memoria (snapshot) sobre la cual hacer se quiere hacer el análisis. Al abrir Android Monitor se presentan 3 gráficas: Memoria, Red y GPU. Para guardar la captura de la memoria se deben utilizar los botones que aparecen justo encima de la gráfica de memoria. Se debe presionar el botón con la esfera para iniciar y se debe presionar de nuevo para terminar la captura.

Para encontrar las capturas tomadas se debe acceder a View/Tool Windows/Captures como lo muestra la siguiente imagen.

Al abrir esta nueva ventana aparecen las carpetas con todas las capturas; aquí debe aparecer la captura que se acaba de realizar.

Cuando se selecciona el archivo de captura se abre una ventana con 3 secciones de información, la sección más grande contiene los métodos que se ejecutaron durante la captura, se puede desplegar cada nodo para ver que métodos de que clase se ejecutan, En la parte inferior izquierda se muestra una gráfica con las ubicaciones y a la derecha un resumen de lo que se tiene actualmente seleccionado de métodos o de la gráfica.

Para mostrar un caso específico de alocación de objetos en memoria se crea una variación del método de Hotspot presentado previamente. En esta variación se crea un recorrido en el que se asignan valores a una variable de tipo String y se guardan estos objetos dentro de una lista.

Al realizar una captura con este método se obtiene el siguiente resultado:

De manera predeterminada se presenta la información de la alocación en memoria ordenada por tamaño. Se ve un grupo grande que ocupa el espacio, al cambiarlo por ordenamiento de conteo, en los selectores de la parte superior, se ve de manera más detallada el grupo que se creó con el método.
Al hacer click sobre una de las barras de la gráfica se puede ver de manera organizada la jerarquía de métodos que finalmente llevaron a ese resultado. En la parte derecha de la vista se puede identificar precisamente el método realizar pedido.

El objetivo primordial es encontrar puntos críticos en la gráfica causados por el desarrollo normal de la aplicación. Con esta herramienta se puede encontrar la línea específica encargada de la alocación.
Al hacer doble click en el método de la vista de la derecha se redirige en la vista principal al thread encargado de la tarea y se obtiene información más detallada del método como el conteo y el tamaño total utilizado por éste.

HPROF
HPROF es una herramienta que ofrece Android para generar archivos con el perfilamento del heap/cpu. Esta herramienta presenta información como las clases, las instancias de cada clase y un árbol de referencias para llevar registro del uso de memoria y las pérdidas de memoria.
Esta vez se debe utilizar el botón que se encuentra a la izquierda de Allocator y tiene una flecha verde apuntando hacia la gráfica.

De nuevo se debe presionar el botón dos veces y ahora en la vista de capturas aparece una nueva carpeta que contiene los archivos del heap. Esta carpeta se llama Heap Snapshot.

Al abrir el snapshot que queda guardado en las capturas se obtienen de nuevo 3 vistas. La vista inferior presenta la estructura arbórea que contiene a los elementos seleccionados. En la parte superior se puede elegir la clase de la que se desean visualizar objetos. La tabla de clases se puede ordenar por cantidad de objetos guardados, conteo en el heap, tamaño y cantidad retenida. En la tabla superior derecha se presentan las instancias de la clase seleccionada, la profundidad a la que se encuentra el elemento y la dominancia que éste tiene.

Para mostrar el uso específico de la herramienta se utiliza de nuevo el caso de creación de una lista de objetos. Se debe crear un nuevo snapshot con la información al crear los objetos y se obtiene el siguiente resultado.

Como se esperaba las clases con mayor espacio utilizado son byte[], char[] y String. Como no solo los strings contienen byte[] esta clase tiene aún más tamaño que la de String. Al seleccionar la clase a la derecha aparecen las distintas instancias de String que tiene la memoria en el momento del snapshot. Desde los números que genero el método hasta los textos de los elementos visuales de la aplicación.

A la derecha de las instancias aparece un nuevo elemento llamado Analyzer Tasks al abrirlo aparece un botón de ejecución. En este elemento se seleccionan el tipo de análisis que se desea realizar e.g. Detect leaked activities, Find duplicate strings. En la vista que aparece en la parte inferior de Analyzer Tasks aparecen los resultados del análisis. Se pueden tomar los resultados para mejorar la cantidad de veces que es necesario guardar el registro de cada String en el heap.

Finalmente, en la parte inferior se pueden ver los datos de la referencia de la instancia del objeto seleccionada. Por ejemplo, al seleccionar un String, aparecen las referencias a este, como la referencia que tiene el ArrayList en el que se agregan todos los elementos.

Resumen
En esta guía se presentan nuevas formas de encontrar información valiosa para mejorar la experiencia del usuario final. En el desarrollo de aplicaciones móviles el objetivo no debe ser solo encontrar la solución al problema, es necesario siempre pensar en el usuario final y en el impacto de cada nueva línea de código. Se espera que estas herramientas ayuden a encontrar puntos críticos de cada proyecto y errores ocultos que deben ser resueltos.
VERSIONES
Versión | Autores | Fecha |
---|---|---|
1.1 | Daniel Ordoñez, Mario Linares Vásquez | 24 de abril, 2017 |
1.2 | Sergio Velasquez | 15 de noviembre, 2017 |