Cada día, las aplicaciones que nos encontramos tienden a ocupar más y más espacio. Cuando en las primeras versiones de Android nos encontrábamos apps de unos 2 MB, ahora es de lo más normal ver que estas aplicaciones han pasado a ocupar de 10 a 20 MB.
Pero, ¿por qué ahora ocupan más?
Esto nos ha llevado a plantearnos la pregunta de dónde están las razones de que ahora ocupen cada vez más las aplicaciones, y por consiguiente, sus APKs. La respuesta no se debe a una única razón, sino a un conjunto de argumentos que se han visto afectados por Android:
- La multiplicación de las categorías dpi: ahora tenemos muchas más variedad de pantallas, para las que debemos cubrir sus recursos. Concretamente tenemos: [l|m|tv|h|x|xx|xxx|dpi]
- La propia evolución de Android, sus herramientas de desarrollo y las librerías disponibles en el ecosistema
- Las expectativas del usuario en base a la alta calidad de las nuevas interfaces gráficas
- …
Es cierto que no en todos los casos podremos evitar ese crecimiento, pero sí podremos intentar controlarlo en la medida de lo posible. Por tanto, llega un momento que una aplicación ligera en Google Play puede suponer una ventaja sobre el resto. ¿Por qué? En primer lugar, porque el código puede ser más simple y sencillo de mantener, lo que puede significar que está mejor desarrollado. En segundo lugar, porque los desarrolladores seguirían luchando por no llegar al límite de 50 MB de Google Play, evitando así tener que descargar extensiones adicionales. Y por último, por las limitaciones que existen: ancho de banda, espacio…
El formato del fichero APK
Lo primero para saber cómo podremos reducir el fichero, es comprender su estructura. Básicamente se trata de un fichero que contiene varios archivos en una forma comprimida. Como tal, podríamos descomprimirlo con el simple comando unzip.
La mayoría de los directorios que podemos ver ya nos resultan familiares como desarrolladores: veremos los recursos, el Manifest, las librerías… Y en classes.dex la versión compilada de nuestro código Java.
Ahora pasaremos a ver algunas técnicas que podemos utilizar para reducir en la medida de lo posible el tamaño de este fichero, pero hay que mencionar que es importante recordar que al tratarse de un fichero comprimido tiene dos tamaños: el comprimido y el descomprimido. Pero, en este caso, nos centraremos en el tamaño comprimido, siempre sin dejar de recordar que cuanto menor es el APK, menor es la versión sin comprimir.
Reduciendo el tamaño del APK
Para empezar hay una mala noticia: cada aplicación es diferente, por lo que no hay una norma absoluta para aligerar nuestro APK. Pero lo bueno es que tenemos 3 componentes importantes sobre los que podemos trabajar de forma sencilla:
- Código fuente Java
- Recursos / Assets
- Código nativo
Limpieza a la hora de programar
El primer paso es conocer el código como la palma de nuestra mano. Esto nos llevará a deshacernos de todo el código inutilizado, así como de librerías que ya hemos dejado de utilizar… De esta forma, como código sólo tendremos lo esencial. Debemos evitar acabar con lo que se llama código muerto, que es ese código obsoleto que ya no utilizamos y hemos olvidado eliminar. Por suerte, podemos contar con algunas herramientas que nos pueden ayudar…
Ejecutar Proguard
Esta herramienta nos permite ofuscar, optimizar y reducir el código en tiempo de compilación. Una de las funciones que realiza, precisamente, es detectar los fragmentos de código que no se utilizan gracias al recorrido que hace en árbol del código, de forma que todo el código no alcanzado (o innecesario) será extraido del APK. Además, nos añadirá funciones de seguridad, pues cambiará el nombre de campos, clases e interfaces, de formas que el código sea más ligero e ilegible ante ingeniería inversa.
«Un gran poder colleva una gran responsabilidad». Y es que Proguard, si no sabemos configurarlo bien para indicarle dónde puede y no actuar, puede llegar a romper algunas aplicaciones debido a la reflexión.
Usar Lint extensivamente
Proguard nos sirve para el código Java, pero si queremos mejorar los recursos, deberemos utilizar Lint, para detectar todos los recursos sin uso, mediante una llamada a ./gradlew lint. Esto generará una lista exhaustiva de los recursos, pudiendo ver los que no están en uso. Pero debemos tener en cuenta que trabaja con los recursos bajo la carpeta res, obviando los que se encuentra en la carpeta assets. Para los ficheros de esta carpeta, deberemos hacerlo manualmente.
Racionar bien los recursos
Es cierto que Android tiene una fragmentación digna de mención. Pero también es cierto que Android ha sido diseñado para soportar los dispositivos independientemente de su configuración (densidad de pantalla, forma, tamaño…) A partir de Android 4.4, incluso se incluyeron nuevas densidades como tvdpi. Pero esto no quiere decir que nuestra app deba soportarlas todas. Tenemos que ser capaces de pensar en qué dispositivos se va a utilizar nuestra app, o incluso pensar en agrupar aquellas densidades, por ejemplo, donde vamos a tener pocos usuarios.
Hay gente que por ejemplo sólo soportan IPAP, xhdpi y xxhdpi, pues Android calcula automáticamente los recursos que faltan escalando el recurso como sea necesario. ¿Por qué estas densidades? En muchos casos, se cubre más del 80% de los usuarios. ¿Por qué no incluir xxxhdpi? Ahora mismo es sólo una prueba de futuro.
Minimizar configuraciones de recursos
Muchas veces, deberemos incluir librerías como la librería Support, o Google Play Services, o el SDK de Facebook… Todas ellas vienen con recursos que no son útiles para nuestra aplicación. Así que para ello podemos utilizar el plugin Android Gradle Plugin 0.7, al cual le pasamos información sobre la configuración de nuestra app de forma que se impide que se incluyan los recursos que no coinciden con nuestra app. Así ahorraremos también espacio.
Comprimir imágenes
AAPT viene con un algoritmo de compresión de imágenes sin pérdida. Esto nos permite reducir el tamaño de los recursos que sí vamos a utilizar, pero no debe impedir que utilicemos también otras herramientas, como pueden ser pngquant, ImageAlpha o ImageOptim. Todo ya dependerá de cuál se adapta mejor a nuestras necesidades.
Pero no sólo ocurre eso con nuestras imágenes genéricas, sino que las imágenes específicas de Android también puede ser optimizadas. Hablamos de las 9-patches. Estas imágenes son las que permitirán estirar imágenes sin deformarlas. A pesar de que no haya herramientas específicas, sí que podemos minimizar las zonas estirables a un mínimo.
Limitar el número de arquitecturas
A pesar de que programamos nuestras aplicaciones en Java, es posible que en algunos casos necesitemos algo de código nativo (NDK). Para ello, también debemos pensar en reducir el código nativo. Normalmente será suficiente con las arquitecturas armeabi y x86.
Reutilizar siempre que sea posible
La reutilización es una de las principales herramientas para optimizar. Para ello, podemos por ejemplo cambiar el color de un asset, o por ejemplo incluso rotar una imagen, con un poco de código en XML. Aquí, podríamos olvidarnos de ic_arrow_collapse, gracias al código que podemos ver debajo. Y esto nos provoca más ahorro en espacio.
Renderizar con código cuando se pueda
Las imágenes son uno de los factores que más afectan al tamaño de nuestra aplicación, y por eso debemos pensar en reducirlas al máximo. Para ello, podemos llegar a plantearnos renderizar por ejemplo las animaciones, evitando tener que montar una animación con una gran cantidad de imágenes, y haciéndolo así por código.
Si en el caso que acabamos de ver, lo hiciéramos cada 16ms, tendríamos una librería con el doble de tamaño… ¿Realmente es viable? Para ello, podemos plantearnos hacer la animación por código, aunque nos lleve tiempo programarla, pero nos permitirá ahorrar mucho espacio mientras mantenemos una animación lo suficientemente suave por ejemplo a 60fps. Aun así, esperamos que Google esté trabajando en un sistema de renderizado de animaciones mucho más optimizado.
¿Ir más lejos?
Todo lo anterior nos permite optimizar la aplicación y librerías desde el punto de vista del desarrollador. Pero, ¿afecta la cadena de distribución en algo? Podríamos estar pensando en algo como que la aplicación sólo incluya librerías que realmente sean necesarias en el dispositivo que se instala la app. Podríamos incluso imaginar que sólo empaquetamos la configuración del dispositivo donde se va a instalar. Pero esto nos llevaría a romper una de las funcionalidades más importantes de Android: la forma en que Android se adapta a los cambios de configuración de forma dinámica.
Esto nos lleva a pensar que el empaquetado en APK por parte de lservidor es muy potente, pero también muy arriesgado pues cada usuario tendrá un paquete diferente del enviado a Play Store, y eso puede llegar a provocar la no garantía de que la app funcione perfectamente.
¿Te influyen en tu opinión los tamaños de las aplicaciones que te instalas?
Via Cyril Mottier