Desarrollo iterativo rápido para Android
En esta página, se describe cómo bazel mobile-install
acelera el desarrollo iterativo para Android. Se describen los beneficios de este enfoque en comparación con los inconvenientes de los pasos de compilación e instalación separados.
Resumen
Para instalar pequeños cambios en una app para Android muy rápidamente, haz lo siguiente:
- Busca la regla
android_binary
de la app que quieres instalar. - Conecta tu dispositivo a
adb
. - Ejecuta
bazel mobile-install :your_target
. El inicio de la app será un poco más lento de lo habitual. - Editar el código o los recursos de Android
- Ejecuta
bazel mobile-install :your_target
. - Disfruta de una instalación incremental rápida y mínima.
Estas son algunas opciones de la línea de comandos de Bazel que pueden ser útiles:
--adb
le indica a Bazel qué objeto binario de adb usar.--adb_arg
se puede usar para agregar argumentos adicionales a la línea de comandos deadb
. Una aplicación útil de esto es seleccionar en qué dispositivo quieres instalar la app si tienes varios dispositivos conectados a tu estación de trabajo:bazel mobile-install :your_target -- --adb_arg=-s --adb_arg=<SERIAL>
Si tienes dudas, consulta el ejemplo, comunícate con nosotros en Google Groups o informa un problema en GitHub.
Introducción
Uno de los atributos más importantes de la cadena de herramientas de un desarrollador es la velocidad: hay una gran diferencia entre cambiar el código y verlo ejecutarse en un segundo, y tener que esperar minutos, a veces horas, antes de obtener comentarios sobre si los cambios hacen lo que esperas.
Desafortunadamente, la cadena de herramientas tradicional de Android para compilar un APK implica muchos pasos monolíticos y secuenciales, y todos ellos deben realizarse para compilar una app para Android. En Google, esperar cinco minutos para compilar un cambio de una sola línea no era inusual en proyectos más grandes, como Google Maps.
bazel mobile-install
acelera mucho el desarrollo iterativo para Android, ya que usa una combinación de reducción de cambios, fragmentación del trabajo y manipulación inteligente de los elementos internos de Android, todo sin cambiar el código de tu app.
Problemas con la instalación de apps tradicionales
Compilar una app para Android tiene algunos problemas, como los siguientes:
Dexing De forma predeterminada, la herramienta Dexer (históricamente
dx
, ahorad8
or8
) se invoca exactamente una vez en la compilación y no sabe cómo reutilizar el trabajo de compilaciones anteriores: vuelve a dexear cada método, aunque solo se haya cambiado uno.Se suben datos al dispositivo. adb no usa todo el ancho de banda de una conexión USB 2.0, y las apps más grandes pueden tardar mucho en subirse. Se sube toda la app, incluso si solo cambiaron partes pequeñas, por ejemplo, un recurso o un solo método, por lo que esto puede ser un cuello de botella importante.
Compilación en código nativo Android L introdujo ART, un nuevo tiempo de ejecución de Android, que compila las apps por adelantado en lugar de compilarlas justo a tiempo como Dalvik. Esto hace que las apps sean mucho más rápidas, pero el tiempo de instalación es más largo. Este es un buen equilibrio para los usuarios, ya que suelen instalar una app una vez y usarla muchas veces, pero genera un desarrollo más lento en el que una app se instala muchas veces y cada versión se ejecuta como máximo un puñado de veces.
El enfoque de bazel mobile-install
bazel mobile-install
realiza las siguientes mejoras:
Desazucarado y dexado fragmentados. Después de compilar el código Java de la app, Bazel fragmenta los archivos de clase en partes de tamaño aproximadamente igual y, luego, invoca
d8
por separado en cada una de ellas. No se invocad8
en las particiones que no cambiaron desde la última compilación. Luego, estos fragmentos se compilan en APKs fragmentados separados.Transferencia incremental de archivos. Los recursos de Android, los archivos .dex y las bibliotecas nativas se quitan del archivo .apk principal y se almacenan en un directorio de instalación móvil independiente. Esto permite actualizar el código y los recursos de Android de forma independiente sin reinstalar toda la app. Por lo tanto, la transferencia de archivos lleva menos tiempo y solo se vuelven a compilar en el dispositivo los archivos .dex que cambiaron.
Instalación fragmentada. La instalación desde dispositivos móviles usa la herramienta
apkdeployer
de Android Studio para combinar los APKs fragmentados en el dispositivo conectado y brindar una experiencia cohesiva.
Dexing fragmentado
El dexing fragmentado es bastante sencillo: una vez que se compilan los archivos .jar, una herramienta los fragmenta en archivos .jar separados de tamaño aproximadamente igual y, luego, invoca d8
en los que se cambiaron desde la compilación anterior. La lógica que determina qué fragmentos se deben convertir en dex no es específica de Android: solo usa el algoritmo general de eliminación de cambios de Bazel.
La primera versión del algoritmo de fragmentación simplemente ordenaba los archivos .class alfabéticamente y, luego, dividía la lista en partes del mismo tamaño, pero esto resultó ser subóptimo: si se agregaba o quitaba una clase (incluso una anidada o anónima), todas las clases que le seguían alfabéticamente se desplazaban en uno, lo que provocaba que se volviera a generar el dexing de esos fragmentos. Por lo tanto, se decidió fragmentar los paquetes de Java en lugar de las clases individuales. Por supuesto, esto aún genera la indexación de muchos fragmentos si se agrega o quita un paquete nuevo, pero eso es mucho menos frecuente que agregar o quitar una sola clase.
La cantidad de fragmentos se controla con la configuración de la línea de comandos, mediante la marca --define=num_dex_shards=N
. En un mundo ideal, Bazel determinaría automáticamente cuántos fragmentos son mejores, pero actualmente Bazel debe conocer el conjunto de acciones (por ejemplo, los comandos que se ejecutarán durante la compilación) antes de ejecutar cualquiera de ellas, por lo que no puede determinar la cantidad óptima de fragmentos porque no sabe cuántas clases de Java habrá finalmente en la app. En términos generales, cuantos más fragmentos haya, más rápida será la compilación y la instalación, pero más lento será el inicio de la app, ya que el vinculador dinámico tendrá que hacer más trabajo. Por lo general, el punto óptimo se encuentra entre 10 y 50 fragmentos.
Implementación incremental
La utilidad apkdeployer
que se describe en "El enfoque de mobile-install" ahora controla la instalación y la transferencia incrementales de fragmentos de APK.
Mientras que las versiones anteriores (nativas) de mobile-install requerían un seguimiento manual de las instalaciones por primera vez y la aplicación selectiva de la marca --incremental
en las instalaciones posteriores, la versión más reciente en rules_android
se simplificó en gran medida. Se puede usar la misma invocación de instalación para dispositivos móviles, independientemente de la cantidad de veces que se haya instalado o reinstalado la app.
A grandes rasgos, la herramienta de apkdeployer
es un wrapper para varios subcomandos de adb
. La lógica principal del punto de entrada se encuentra en la clase com.android.tools.deployer.Deployer
, con otras clases de utilidad ubicadas en el mismo paquete.
La clase Deployer
admite, entre otras cosas, una lista de rutas de acceso a los APK divididos y un archivo .proto con información sobre la instalación, y aprovecha las funciones de implementación de los Android App Bundles para crear una sesión de instalación y, luego, implementar de forma incremental las divisiones de la app.
Consulta las clases ApkPreInstaller
y ApkInstaller
para obtener detalles sobre la implementación.
Resultados
Rendimiento
En general, bazel mobile-install
genera una aceleración de 4 a 10 veces en la compilación y la instalación de apps grandes después de un pequeño cambio.
Los siguientes números se calcularon para algunos productos de Google:
Por supuesto, esto depende de la naturaleza del cambio: la recompilación después de cambiar una biblioteca base lleva más tiempo.
Limitaciones
Los trucos que realiza la aplicación de código auxiliar no funcionan en todos los casos. En los siguientes casos, se destaca dónde no funciona según lo esperado:
La instalación en dispositivos móviles solo se admite a través de las reglas de Starlark de
rules_android
. Consulta el "breve historial de la instalación en dispositivos móviles" para obtener más detalles.Solo se admiten dispositivos que ejecutan ART. La instalación desde dispositivos móviles usa funciones de API y de tiempo de ejecución que solo existen en dispositivos que ejecutan ART, no Dalvik. Cualquier entorno de ejecución de Android más reciente que Android L (API 21+) debería ser compatible.
Bazel debe ejecutarse con un entorno de ejecución de Java y una versión de lenguaje de herramientas 17 o superior.
Las versiones de Bazel anteriores a la 8.4.0 deben especificar algunas marcas adicionales para mobile-install. Consulta el instructivo de Bazel para Android. Estos parámetros indican a Bazel dónde se encuentra el aspecto de instalación para dispositivos móviles de Starlark y qué reglas se admiten.
Breve historia de las instalaciones en dispositivos móviles
Las versiones anteriores de Bazel incluían de forma nativa reglas integradas de compilación y prueba para lenguajes y ecosistemas populares, como C++, Java y Android. Por lo tanto, estas reglas se denominaron reglas nativas. Bazel 8 (lanzado en 2024) quitó la compatibilidad con estas reglas porque muchas de ellas se habían migrado al lenguaje Starlark. Consulta la entrada de blog sobre la versión 8.0 LTS de Bazel para obtener más detalles.
Las reglas nativas heredadas de Android también admitían una versión nativa heredada de la funcionalidad de instalación en dispositivos móviles. Ahora se conoce como "instalación desde dispositivos móviles v1" o "instalación desde dispositivos móviles nativa". Esta funcionalidad se borró en Bazel 8, junto con las reglas integradas de Android.
Ahora, todas las funciones de instalación en dispositivos móviles, así como todas las reglas de compilación y prueba de Android, se implementan en Starlark y residen en el repositorio de GitHub de rules_android
. La versión más reciente se conoce como "mobile-install v3" o "MIv3".
Nota sobre la nomenclatura: En un momento, Google tenía disponible de forma interna una "instalación móvil v2", pero nunca se publicó de forma externa, y solo se sigue usando la versión 3 para la implementación de reglas_android internas de Google y de OSS.