instalación para dispositivos móviles de Bazel

Informar un problema Ver fuente Por la noche · 7.2 · 7.1 · 7.0 · 6.5 · 6.4

Desarrollo iterativo rápido para Android

En esta página, se describe cómo bazel mobile-install hace que el desarrollo iterativo para Android. Describe las ventajas de este enfoque frente a desafíos del método tradicional de instalación de aplicaciones.

Resumen

Para instalar cambios pequeños en una app para Android muy rápido, haz lo siguiente:

  1. Busca la regla android_binary de la app que quieras instalar.
  2. Inhabilita ProGuard quitando el atributo proguard_specs.
  3. Establece el atributo multidex en native.
  4. Establece el atributo dex_shards en 10.
  5. Conecta tu dispositivo con ART (no Dalvik) a través de USB y habilita el USB. depurar en él.
  6. Ejecuta bazel mobile-install :your_target. El inicio de la app será un poco más lento de lo normal.
  7. Edita el código o los recursos de Android.
  8. Ejecuta bazel mobile-install --incremental :your_target.
  9. Disfruta de no tener que esperar mucho.

Estas son algunas opciones de línea de comandos para Bazel que pueden ser útiles:

  • --adb le indica a Bazel qué objeto binario de adb debe usar.
  • Se puede usar --adb_arg para agregar argumentos adicionales a la línea de comandos de adb. Una aplicación útil de esta función es seleccionar el dispositivo que deseas instalar si tienes varios dispositivos conectados a tu estación de trabajo: bazel mobile-install --adb_arg=-s --adb_arg=<SERIAL> :your_target
  • --start_app inicia la app automáticamente.

Si tienes dudas, consulta ejemplo o comunícate con nosotros.

Introducción

Uno de los atributos más importantes de la cadena de herramientas de un desarrollador es la velocidad. es una gran diferencia entre cambiar el código y ver que se ejecuta en un segundo y tener que esperar minutos, a veces horas, antes de recibir comentarios dependiendo de si los cambios hacen lo que esperas.

Lamentablemente, la cadena de herramientas tradicional de Android para compilar un .apk implica muchos pasos monolíticos y secuenciales, y todos deben realizarse para compilar una app para Android. En Google, esperar cinco minutos para crear una línea fue algo inusual en proyectos más grandes, como Google Maps.

bazel mobile-install hace que el desarrollo iterativo para Android sea mucho más rápido, ya que con una combinación de reducción de cambios, fragmentación de trabajos y manipulación inteligente de Componentes internos de Android, todo sin cambiar el código de tu app.

Problemas con la instalación de apps tradicionales

La compilación de una app para Android tiene algunos problemas, como los siguientes:

  • Conversión a DEX. De forma predeterminada, “dx” se invoca exactamente una vez en la compilación y no cómo reutilizar el trabajo de compilaciones anteriores: convierte a DEX en cada método nuevamente, incluso aunque solo se cambió un método.

  • Subiendo datos al dispositivo. adb no usa todo el ancho de banda de un puerto USB 2.0 y las apps de mayor tamaño tardarán mucho tiempo en subirse. Toda la aplicación subir, incluso si solo cambiaron partes pequeñas, como un recurso o un único método, por lo que puede ser un gran cuello de botella.

  • Compilación en código nativo. Android L presentó ART, un nuevo tiempo de ejecución de Android, que compila apps con anticipación en lugar de compilarlas justo a tiempo, como Dalvik Esto hace que las apps sean mucho más rápidas, a costa de una instalación más larga. tiempo. Esta es una buena compensación para los usuarios, ya que suelen instalar una app. una vez y lo usan muchas veces, pero esto hace que el desarrollo sea más lento se instala varias veces y cada versión se ejecuta, como máximo, varias veces.

El enfoque de bazel mobile-install

bazel mobile-install implementa las siguientes mejoras:

  • Conversión a DEX fragmentada. Después de compilar el código Java de la app, Bazel fragmenta la clase. archivos en partes de aproximadamente el mismo tamaño e invoca a dx por separado en de ellos. dx no se invoca en los fragmentos que no cambiaron desde la última compilación.

  • Transferencia incremental de archivos. Recursos de Android, archivos .dex y aplicaciones nativas se quitan del .apk principal y se almacenan en un archivo directorio de instalación para dispositivos móviles. Esto permite actualizar el código y los eventos de recursos de forma independiente sin reinstalar toda la app. Por lo tanto, transferir archivos lleva menos tiempo y solo los archivos .dex que tienen los cambios se volverán a compilar en el dispositivo.

  • Carga de partes de la app desde fuera del .apk. Una pequeña aplicación auxiliar es Se coloca en el .apk que carga recursos de Android, código Java y código nativo. desde el directorio de instalación móvil en el dispositivo y, luego, transfiere el control al app real. Todo esto es transparente para la app, excepto en algunos casos excepcionales. que se describe a continuación.

Dex fragmentado

La conversión a DEX fragmentada es bastante sencilla: una vez que se compilan los archivos .jar, se crea un herramienta los fragmenta en archivos .jar separados de tamaño similar; luego, los invoca dx en aquellas que se modificaron desde la compilación anterior. La lógica que determina qué fragmentos a DEX no son específicos de Android: solo usa el algoritmo general de reducción de cambios de Bazel.

La primera versión del algoritmo de fragmentación simplemente ordenó los archivos .class alfabéticamente y luego cortaba la lista en partes de igual tamaño, pero esto demostró ser subóptimo: si se agrega o quita una clase (incluso una clase anidada o uno), haría que todas las clases alfabéticamente cambiaran de a una, lo que provocará nuevamente la conversión a DEX de esos fragmentos. Por lo tanto, se decidió fragmentar Java paquetes, en lugar de clases individuales. Por supuesto, esto sigue dando como resultado convertir a DEX muchos fragmentos si se agrega o quita un paquete nuevo, pero eso es mucho menos frecuentes que agregar o quitar una sola clase.

El archivo BUILD controla la cantidad de fragmentos (con el android_binary.dex_shards). En un mundo ideal, Bazel determinar automáticamente cuántos fragmentos son mejores, pero Bazel actualmente debe saber el conjunto de acciones (por ejemplo, comandos que se ejecutarán durante la compilación) antes de ejecutar cualquiera de ellos, por lo que no puede determinar la cantidad óptima de fragmentos porque no sabe cuántas clases de Java habrá en el futuro . En términos generales, cuantos más fragmentos, más rápido es la compilación y instalación, pero el inicio más lento de la app se vuelve, porque la el vinculador tenga que trabajar más. Por lo general, el punto ideal es entre 10 y 50 gemas.

Transferencia de archivos incremental

Después de compilar la app, el siguiente paso es instalarla, preferentemente con la menor esfuerzo posible. La instalación consiste en los siguientes pasos:

  1. Cómo instalar el .apk (generalmente con adb install)
  2. Cómo subir los archivos .dex, los recursos de Android y las bibliotecas nativas a la directorio de instalación para dispositivos móviles

No hay mucha incrementalidad en el primer paso: la app está instalada o no. Actualmente, Bazel depende del usuario para indicar si debe realizar este paso. a través de la opción de línea de comandos --incremental porque no puede determinar en todos los casos si es necesario.

En el segundo paso, se comparan los archivos de la app de la compilación con una de manifiesto que enumera los archivos de la app que se encuentran en el dispositivo y su sumas de comprobación. Los archivos nuevos se suben al dispositivo y los archivos que cambiaron se actualicen y los archivos que se hayan eliminado se borrarán dispositivo. Si el manifiesto no está presente, se supone que todos los archivos deben que se suba.

Ten en cuenta que es posible engañar al algoritmo de instalación incremental cambiar un archivo en el dispositivo, pero no su suma de comprobación en el manifiesto. Esto podría protección mediante el cálculo de la suma de comprobación de los archivos en el pero se consideró que no valía la pena el aumento en el tiempo de instalación.

La aplicación Stub

La aplicación stub es donde la magia para cargar los dex, el código nativo y Se ejecutan los recursos de Android del directorio mobile-install en el dispositivo.

La carga real se implementa mediante la subclasificación de BaseDexClassLoader y es una con una técnica razonablemente bien documentada. Esto sucede antes de que se ejecute de forma predeterminada para que cualquier clase de app que esté en el APK se pueda Se colocan en el directorio mobile-install del dispositivo para que se puedan actualizar. sin adb install.

Esto debe suceder antes de que se cargan las clases de la app, de modo que no es necesario que ninguna clase de app esté en el .apk. Esto significa que los cambios en esas clases requerirían una versión la reinstala.

Esto se logra reemplazando la clase Application especificada en AndroidManifest.xml con el aplicación stub. Esta toma el control del inicio de la app y modifica el cargador de clases de recursos de forma adecuada en el primer momento (su constructor), utilizando Reflexión de Java sobre los aspectos internos del framework de Android.

Otra cosa que hace la aplicación auxiliar es copiar las bibliotecas nativas instalada por instalación móvil en otra ubicación. Esto es necesario porque el el vinculador dinámico necesita que se configure el bit X en los archivos, lo que no es posible para cualquier ubicación a la que pueda acceder un adb no raíz.

Una vez que se hace todo esto, la aplicación stub crea una instancia de clase Application real, lo que cambia todas las referencias a sí misma por el valor aplicación dentro del framework de Android.

Resultados

Rendimiento

En general, bazel mobile-install da como resultado una aceleración de 4 a 10 veces en la compilación. ni instalar apps grandes después de un pequeño cambio.

Se calcularon las siguientes cifras para algunos productos de Google:

Esto, por supuesto, depende de la naturaleza del cambio: una recompilación después de cambiar una biblioteca base lleva más tiempo.

Limitaciones

Los trucos que reproduce la aplicación auxiliar no funcionan en todos los casos. Los siguientes casos destacan los casos en los que no funciona según lo esperado:

  • Cuando se transmite Context a la clase Application en ContentProvider#onCreate() Se llama a este método durante la aplicación inicio antes de que podamos reemplazar la instancia de Application por lo que ContentProvider seguirá haciendo referencia a la aplicación auxiliar. en lugar de la real. Podría decirse que no es un error, debería reducir Context de esta manera, pero parece suceder en unos pocos en Google.

  • Los recursos que instaló bazel mobile-install solo están disponibles en la aplicación. Si otras apps acceden a los recursos mediante PackageManager#getApplicationResources(), estos recursos provendrán del última instalación no incremental.

  • Dispositivos que no ejecutan ART. Si bien la aplicación auxiliar funciona bien en Froyo y, luego, Dalvik tiene un error que hace pensar que la app está es incorrecto si el código se distribuye en varios archivos .dex en ciertos archivos casos, por ejemplo, cuando las anotaciones de Java se usan en una específicas de una nueva manera. Siempre que tu app no genere señales de estos errores, debería funcionar con Dalvik, (ten en cuenta, sin embargo, que la compatibilidad con versiones anteriores de Android no es exactamente nuestro enfoque)