El límite de los 64k métodos en ficheros DEX, ¿estamos cerca de la solución?
Para todos los que son programadores de Android, existe un límite el cual quizás muchos de ellos no conocían hasta hoy. Este límite viene provocado por el formato de los ficheros DEX que se utilizan. Estos ficheros son el formato binario utilizaro por la máquina virtual Dalvik que se utiliza en los dispositivos Android.
Y presentan un límite de 64k para referencia de métodos. De hecho, la causa de esta limitación viene porque los métodos se referencian utilizando un índice, el cual se representa con 16 bits, o 65536 en decimal que es lo mismo (lo que son 64k). Esto nos lleva a que no podremos tener más de 65536 métodos en nuestra aplicación. Esto hará que muchas grandes aplicaciones lleguen a este límite y se puedan ver afectadas, aunque muchas de ellas no alcanzarán el límite. Pero el problema empieza cuando utilizamos librerías de terceros, y ahí Google es uno de los que más métodos utilizan. Por eso, deberíamos esperar una solución por parte de Google antes que tarde.
La librería Google Play Services, el primer escollo
Hace tiempo que Google estableció una política de que parte de sus características nuevas no serían añadidas a la plataforma Android, sino puestas a disposición a través de sus aplicaciones básicas, que encontraremos en todos los dispositivos Android con certificación Google. De hecho, la más importante es Google Play Services, pues ofrece soporte para muchos servicios de Google (Google+, Gmail…), así como funcionalidades como localización. Por eso, en muchos casos nos interesará utilizar utilizar esta librería.
Y el primer problema aparece ahí: cuando no pensábamos que pudiéramos llegar al delicado límite de 64k, sólo el hecho de añadir Google Play Services, en su versión 5, nos incrementará el número de referencias de métodos en 20k. Y posiblemente, sólo necesitemos una ínfima parte de éstos. ¿Sería una solución que Google dividiera su librería para que sólo importemos lo que nos interesa? Es una opción, pero por ahora no es así.
Librerías allá por donde vamos
Y bueno, si sólo necesitásemos añadir esta librería vale, pero imagina que ahora seguimos añadiendo librerías como Google Fit o Android Wear… Parece que el límite puede no estar tan lejos. Hay otras tantas librerías que utilizaremos que subirán sustancialmente esta crítica cifra, como por ejemplo Guava, con un total de casi 15.000 métodos, o otras librerías de soporte como ActionBarSherlock o para determinadas funcionalidades como puede ser Retrofit o GSON, entre otras. Así que empieza a no ser para nada descabellado pensar en alcanzar el límite de 64k métodos.
Y no podemos caer en la trampa que utilizar librerías de terceros es una mala idea (siempre que sean buenas librerías), pues es una tontería reinventar la rueda. De hecho, muchas de estas librerías están bien testeadas por mucha gente o incluso mucha gente a contribuido a optimizarla. Pero quizá deberíamos empezar a pensar en que el límite 64k puede empezar a tener relación con una limitación en la cantidad de librerías de terceros que podemos utilizar.
¿Google al rescate?
Google es consciente de esta realidad y ya han publicado un post en el que explican una forma de resolver esto, o al menos parcialmente. La idea básica es construir varios ficheros DEX en lugar de un único fichero DEX enorme (con todo nuestro código y de las librerías importadas). Como el límite es en el fichero DEX y no en la máquina virtual, al dividir el código en varios ficheros podremos utilizar ya más métodos (y de forma paralela más librerías si lo deseamos).
Si hemos comentado que Google en el post ha resuelto el tema «parcialmente», es porque en él nos explican cómo leer un segundo fichero DEX desde la carpeta assets de nuestra aplicación y cargarlo usando un DexClassLoader. Pero en ningún momento nos hablan sobre cómo dividir el código en varios ficheros DEX. Además, el artículo es anterior a que apareciera Gradle, por lo que no está adaptado a este proceso.
Y no todas las pegas terminan ahí, pues las clases de carga en tiempo de ejecución tienen consecuencias cuanto menos curiosas en nuestro código: el cargador de clases base que utiliza nuestra aplicación no verá las clases en el segundo fichero DEX hasta que éste haya sido cargado correctamente. Esto nos lleva a que no podamos instanciar una clase (crear un objeto) con el comando new, tal como estamos acostumbrados a hacer, al menos hasta que el segundo fichero DEX esté cargado.
Ahora sí, ¿ProGuard al rescate?
ProGuard es una de esas herramientas que todo desarrollador de Android debería conocer, pues nos permite cosas como por ejemplo ofuscar código, así como eliminar clases, métodos y campos que no utilizamos, ya sea explícita o implícitamente. ¿Todo resuelto?
A pesar de que ProGuard puede ser una buena solución temporal, no creo que como programadores queramos tener que ejecutar ProGuard durante el desarrollo cuando simplemente estamos probando y depurando la aplicación. Si llegásemos a este caso, depurar nuestro código sería mucho más difícil, sobre todo por la ralentización que sufriríamos en la compilación.
Y todo esto, en el caso de que no utilicemos más de esos 64k métodos. Pero, ¿y si los importamos pero es que además los utilizamos? Aquí empieza un debate de lo más interesante:
- ¿Está una app bien diseñada si necesita utilizar más de 64k métodos?
- ¿Realmente es necesario tanto código para una app?
La respuesta quizá la podamos encontrar en aplicaciones como Facebook, pues es una aplicación con suficientes características como para tener que afrontar este problema. Y no sólo ellos, pues algunas aplicaciones de Yahoo han pasado este límite y han tenido que limitar su funcionalidad para quedar justo por debajo, tal como comenta Jean-Baptiste Quéru en Google+, trabajador de Yahoo y antiguo director de AOSP en Google.
Su artículo es bastante interesante de leer, pues nada más que el comienzo ya llama la atención con su:
Ha las cosas como Google dice, no como Google hace
Esto nos lleva a pensar, ¿le ha pasado este mismo problema a Google con Google Drive, de ahí que tengamos varias aplicaciones? Quizá por eso empieza a ser común que grandes empresas como Facebook dividan sus aplicaciones en otras menores (Facebook, Facebook Messenger…) para poder evitar este problema. Quizá no ha sido por este motivo, pero desde luego nos lleva a pensarlo, al menos. Aunque segmentar una aplicación en varias, puede que no en todos los casos tenga sentido.
Ahora sí que sí, ¿ART al rescate?
Mucha gente esperaba que la introducción de la nueva máquina virtual ART arreglaría este problema. Pero como ya hemos dicho anteriormente, el problema está en el fichero DEX, que se genera en tiempo de compilación, y no en la máquina virtual. Por tanto, a pesar de todas las mejoras que aporta ART, no tiene la solución a este problema.
Entonces, ¿qué?
Google ha hecho alusión a un formato DEX actualizado, el cual podría hacer frente a este problema. Pero deberíamos ser conscientes de que esto no haría frente al problema en versiones de Android anteriores, pues no serían capaz de utilizar este formato.
Y todo esto ha aparecido recientemente, tras bastante tiempo desde la solución de utilizar varios ficheros DEX, cuando en un podcast de Google hacían una breve mención a que una solución podría estar en camino. Pero además, otro indicio de que una solución puede estar en camino, gracias a dos repositorios Git que se han encontrado, bastante indocumentados por cierto, donde parecen ofrecer una solución que separa el código Java compilado en varios ficheros DEX, empaquetándolos después en el APK y parcheando el cargador de clases para que cargue todas las clases. Si todo esto parece confirmarse, esto sí sería una posible solución para el problema del límite de 64k métodos.
Y como estos respositorios forman parte del AOSP, es de esperar que tengamos noticias pronto (quizá en forma de post) por parte de Google. ¿Será esta la solución definitiva al problema?
Más información HellSoft