Minimizando dependencias en Android

Es común que cuando nos encontramos frente a un problema lo primero que atinamos es a buscar una biblioteca que ya provea una solución. Si bien es una buena práctica (Don’t repeat yourself) muchas veces esta solución trae más problemas a futuro que soluciones.

Un caso particular que se suele ver mucho en Android es la necesidad de hacer la experiencia del usuario más rica, de darle más información en pantalla. Para ellos agregamos toda clase de detalles a nuestras UIs, y como escribir vistas desde cero suele ser complejo, terminamos incluyendo una biblioteca como dependencia seleccionada de alguna de las opciones populares.

En general estoy de acuerdo con esa aproximación, pero siempre está el fantasma de los 65K de Android. ¿Entonces qué hacemos?. Simple, escribamos nuestras propias vistas donde sea fácil, trivial o simplemente tenga sentido.

Cabe mencionar que otra posible táctica a este problema es utilizar ProGuard, a fin de eliminar todos los métodos y clases que no se usen. Trataremos de analizar en un próximo artículo esta posibilidad.


Indicador de página

Para ilustrar el punto voy a utilizar un problema con el que me encontré esta semana : agregar a un ViewPager los ‘puntitos’ que indican de manera visual en qué página estoy y cuantas páginas tengo.

Indicardor de páginas de un ViewPager

Si quisiera agregar esto rápidamente al proyecto, con hacer una simple búsqueda en Google encontraría varias opciones. Tal vez una de las más populares es ViewPagerIndicator escrita por Jake Wharton.

Esta biblioteca nos ofrece gran variedad de configuraciones y opciones : círculos, líneas, imágenes personalizadas, con tabs, sin tabs, etc. Yo en principio no necesito tantas cosas. Solo quiero un círculo por página y que el círculo que representa la página actual sea de otro color.

Para hacer el problemas aún más interesante, en esta aplicación estoy llegando a los 62k métodos, lo que me deja poco para maniobrar en cuanto a dependencias se refiere.

Haciendo una cuenta rápida la biblioteca tiene 9 archivos Java, si suponemos que cada uno es una clase y cada uno puede tener alrededor de 30 métodos, estamos casi en 300 métodos solo para mostrar 3 círculos de color blanco.

Vamos a ver a continuación que en unos 12 podemos implementar lo mínimo e indispensable para este proyecto.

Nota : Contar los métodos no es tan lineal, las cuentas son solo a fin de ilustrar el punto. Si te interesa saber que pasa con Java en Android abajo del capot, podés ver la charla de Jake Wharton ‘Exploring Java’s Hidden Costs’.

PagerIndicator.java

Para implementar un indicador de página no necesitamos mucho :

  • Saber cuantas páginas tenemos
  • Saber cuál es la página actual
  • Saber el tamaño de la vista y de los círculos
  • Saber cuando el usuario cambia de página

Lo primero que debemos hacer es crear el esqueleto base de nuestra vista con los constructores que necesita. Cuando extendemos View nos pide que definamos un constructor. Si bien con solo definir el primero alcanzaría para este caso, siempre es bueno definirlos todos, sobre todo si queremos agregar luego la posibilidad de customizar la vista desde el layout XML.

https://gist.github.com/Gazer/a3e8f61031a013aab690c75aeccb0b60

Antes de empezar a dibujar algo vamos a necesitar algunos objetos básicos. Como regla general es malo crear instancias de cualquier cosa en el ciclo de dibujo. Recuerden que debemos poder mantener 60fps en caso de querer animar y si el garbage collector está en el medio eso no es alcanzable.

Este método setup() que creamos será llamado desde todos los constructores que definimos anteriormente, de manera que no importe como se cree la vista tendremos todo lo necesario. Los objetos Paint nos permiten definir cómo se van a dibujar las cosas.

https://gist.github.com/Gazer/5098ec213709d3bac7263d657cca6188

Dibujar los círculos que representan las páginas es bastante simple en este caso. Lo que hago es calcular el ancho total que van a ocupar los N puntos, contando los espacios de separación que son proporcionales al diámetro y luego centro el bloque completo tanto en dirección horizontal como vertical.

Un punto crucial en toda vista es definir el tamaño. En Android un programador puede decir que quiere que una viste ocupe todo el padre, que se adapte al tamaño del contenido, que tenga un tamaño exacto. Esto tanto vertical como horizontal. Una forma de resolver este problema es definir el tamaño mínimo en cada eje y luego ver que es lo que quiere el programador. Una vez que tenemos ambos, nos quedamos con la mejor opción.

Todos estos cálculos se hacen en la pasada de layout, específicamente la llamada a onMeasure es la que “mide” la vista y le avisa al padre el tamaño que vamos a tener.

Siempre que calculemos el tamaño de nuestra vista es fundamental tener en cuenta el padding que el programador está deseando para nuestra vista. En este caso nuestra vista, por ejemplo, queremos que sea como mínimo el diámetro elegido más el padding.

https://gist.github.com/Gazer/6ecbed1b078ac0c2a07608e533698c13

Ya con todas las piezas en su lugar podemos proceder a escribir el método onDraw de nuestra vista, que es donde finalmente se pone interesante.

Todas las vistas en Android son utilizables no solo en nuestras aplicaciones, sino que también podemos verlas en el editor visual del Android Studio. Es por eso que el framework nos da la posibilidad de preguntar en que contexto se va a dibujar la vista. En este caso lo que se hizo fue dibujar 5 círculos con el del medio como página actual en el editor, de manera de poder tener una buena vista previa de la vista en la ventana.

https://gist.github.com/Gazer/dc1f39f6c5787a0d19240350855b5ca2

Como último paso y no por eso menos importante, nos falta conectar con el ViewPager y saber cuando éste último cambia de página. Para eso basta con agregar un PageChangeListener y el resto es historia 🙂

https://gist.github.com/Gazer/09c80557fda8b9d2b91d6953d5afbec4


Como se puede ver hay casos que son muy simples de resolver y evitar así una dependencia externa.

A esta implementación se le pueden mejorar un par de cosas :

  • Utilizar attributes para configurar el radio, color, forma
  • Utilizar el onPageScrolled para animar el cambio de página

Pero eso queda como desafío para el lector :). El código completo de la clase se los dejo en este Gist.

Anuncios

“Funciona” no es suficiente.


No es raro en esta profesión, al revisar el trabajo de otro encontrarse con algo que no es del todo semánticamente correcto o que puede escribirse de mejor manera. Tampoco es raro que nos digan “pero así funciona”. Funciona no es, o no debería al menos, ser suficiente.

En el desarrollo de software existen muchos otros factures a considerar a futuro que no siempre tienen que ver con el funcionamiento actual de la aplicación : ¿el código se entiende?, ¿es eficiente?, ¿es fácil de extender si debemos agregar funcionalidades?. Quizás el aspecto más importante desde el punto comercial es que tanto cuesta corregir un error detectado, ya que en códigos escritos de manera pobre, corregir un error y la posibilidad que aparezca uno nuevo suele ser mayor en los casos donde no se tiene una buena disciplina.

En las siguientes secciones trataré de comentar lo que me ha servido a lo largo de los años y qué herramientas podemos utilizar. Algunos ejemplos serán específicos de Android, pero se pueden extrapolar a cualquier tecnología.


Métricas, métricas y más métricas

Un aspecto importante para saber dónde estamos parados y hacia dónde vamos es la utilización de métricas en nuestro código. Estas métricas pueden ser muy variadas, desde contar a mano cuántas líneas de código hay por archivo, clase o método, hasta medir la complejidad ciclomática de nuestro código.

Las métricas deben ser una herramienta que nos orienten, no que guíen nuestro desarrollo. Es decir, no deberían condicionar si o si la forma de escribir una porción de código porque la métrica da peor en ese caso.

Esto hace que las métricas en si mismas no sean lo importante. Lo que nos debería importar es la tendencia de las mismas. Para poder medir tendencia entonces debemos poder consultar un histórico, por lo que con cierta regularidad deberíamos calcular esas métricas, anotarlas y ponerlas en un gráfico.

Siempre que la tendencia sea negativa es un buen signo, que se mantenga no está mal. Si la curva crece con pendiente leve en un período corto y luego se estabiliza o baja no debería ser preocupante.

Nuestros puntos de alarma deberán ser crecidas de más de un 30/40% de golpe entre una medición y otra. En general cuando hay picos de crecimiento es porque se está trabajando en alguna característica nueva y todavía no se empezó la etapa de estabilización. Lo importante es atacar el problema en ese momento y ver donde podemos mejorar.

Automatización : la clave para no abandonar

En la teoría esto de las métricas es fantástico. Hasta que llega nuestro Manager o Jefe y nos dice “pero teníamos que salir ayer a producción” y todo se va al diablo :). Escribimos lo que podemos para que “simplemente funcione”, no tomamos métricas y a veces ni probamos :).

Luego de dos o tres veces que salimos del apuro nuestro código seguramente ya sea inmanejable y si calculábamos las métricas a mano perdimos historial importante para ver la evolución.

La automatización de esta tarea es crucial, ya que nos permite ir calculando de manera automática y de manera periódica la salud del proyecto.

Para automatizar es que existen los servidores de “Integración Continua” (o CI de sus siglas en inglés) como Jenkins o Travis. Estos son lo suficiente flexibles para ser configurados según nuestras necesidades y contienen cientos de plugins que nos harán la vida más fácil.

En el caso de Jenkins podemos usar los plugins de “Android Lint” y “Report violations” para recolectar métricas y que nos haga un gráfico de la evolución de las mismas.

Personalmente no me gusta que el proyecto se marque como “fallado” si las métricas no están en cero. Prefiero simplemente un aviso de que la aplicación está “inestable” y definir yo si tengo un problema o no.

Métricas en Jenkins

Como punto final, queda definir en cada caso cada cuánto corres las métricas. Podría ser por hora, a una hora fija, por semana, etc. Dependerá del proyecto.

Yo prefiero calcularla en “tiempo real” cada vez que alguien sube código al proyecto. Para eso solo debemos configurar el servidor de CI para que detecte que hay un nuevo código, lo baje y corra las métricas.

Las Herramientas

Para ir terminando por hoy vamos a mencionar algunas de las herramientas que podemos usar para medir métricas :

  • FindBugs : Java/Android. Detecta errores comunes y recomienda buenas prácticas
  • PMD : Java/Android/Objective-C. Analiza el código en busca de variables sin uso, condiciones que se pueden simplicficar, objetos que estamos creando de más, etc.
  • Lint : Existe un programa “lint” casi por cada lenguaje/framework que existe. Existen desde 1979, usalo :)!
  • Codenarc : Groovy/Grails. Permite analizar malas práticas, errores de estilo, etc.
  • Checkstyle : Java/Android. Chequeos básicos para escribir Java con buenas prácticas (nombres de variables, organización del código, etc).
  • Clang Static Analyzer : Objective-C/iOS. Similar a Checkstyle.

Siempre se puede mejorar …

Para cerrar, simplemente eso. Siempre se puede mejorar. Lleva tiempo? Al principio un poco, si. Pero una vez que el camino está sentado el proyecto avanza solo.

A medida que veamos los warnings de las herramientas iremos recordándolas, entonces al escribir el código antes de todo ya lo haremos pensando en buenas prácticas, minimizando el problema desde el principio.

Por último y no por eso menos importante, nuevamente decirles que estas herramientas deben ser una guía!, no deberíamos buscar el Santo Grial de tener en cero nuestras métricas, eso podría llevarnos a lugares peores que no tener métricas. Vean tendencias y saquen conclusiones 🙂

Hacer Aplicaciones Android Mantenibles

Una de las cosas más complicadas a lo largo del tiempo es poder mantener el código fuente de una aplicación Android en un nivel de simplicidad que nos permita a futuro introducir nuevas mejoras, arreglar errores de manera más simple o solamente poder leer el código y decir “ah, si, esto es lo que hace acá”. No siempre lo logramos, ya sea por impericia (todavía no sabemos hacerlo mejor) o por tiempo (nos apuran y en lugar de pensar la mejor manera simplemente escribimos el primer código que se nos ocurre para lograr sacar en tiempo la aplicación).

En general los problemas de impericia se solucionan con capacitación, lectura y pruebas. Es mi idea ir contando varias soluciones que vengo aplicando ya hace un tiempo en cuanto a libraría, patrones y técnicas que nos ayudan día a día a crear mejores productos de software.

En los problemas de tiempo lamentablemente no puedo ayudarlos, ya depende de cada organización y de quién está arriba de nosotros :), pero si sepan que teniendo un buen set de herramientas y saber usarlas, soluciona parte del problema ya que no deberíamos tener que alejarnos de nuestras buenas prácticas.

Para ir finalizando esta introducción, quiero contarles un poco de qué voy a hablar en futuras entradas.

Lo primero que vamos a tratar es cómo manejar el proyecto. Que herramientas podemos usar para medir “calidad”. Luego vamos a seguir con algunos patrones que nos serán de utilidad y bibliotecas que nos ayudarán a seguir esos patrones. Y para terminar vamos a hablar un poco de testing automatizado y que ventajas hay haber utilizado todo lo anterior.

No les puedo dar fechas exactas de cuando iré escribiendo sobre todo esto, pero si les quiero dejar un post en inglés por si quieren ir viendo de que se trata. Es apenas una ápice de lo que la comunidad Android está escribiendo sobre cómo deberían estar tratándose los desarrollos y cómo sumar calidad a tus aplicaciones.

http://artemzin.com/blog/android-development-culture-the-document-qualitymatters/