Join FlipAndroid.COM Telegram Group: https://t.me/joinchat/F_aqThGkhwcLzmI49vKAiw


Activar la opción `–multi-dex` en hormiga para Android

Es fácil habilitar la opción multi-dex para el sistema de generación de grados, pero no he encontrado el ejemplo de cómo puedo habilitar esta opción para el edificio de hormigas. ¿Cómo puede archivar esto?

  • El número de referencias de método en un archivo .dex no puede exceder 64k API 17
  • INSTALL_FAILED_DEXOPT Error en Android 5.0 en modo de liberación
  • Pruebas unitarias Robolectric fallan después de Multidex
  • Android gradle plugin multidex ZipException
  • Estudio de Android java.lang.NoClassDefFoundError: android.support.v4.app.NavUtilsJB
  • No se puede instalar mi aplicación recientemente lanzada: Código de error: -504
  • Utilizar multiDexEnabled sin Gradle pero proceso de generación de Eclipse en su lugar
  • La prueba de instrumentación falla al azar con multidexado activado
  • 3 Solutions collect form web for “Activar la opción `–multi-dex` en hormiga para Android”

    Tenemos 2 opciones:

    1. Cambiar DexExecTask [introducir un nuevo parámetro para multi dex], compilar ant.jar, utilizar este tarro para la construcción. No me gusta esta opción, porque tenemos que proporcionar ant.jar actualizado para todos los miembros del equipo.
    2. Modifique el archivo build.xml del proyecto. He encontrado el archivo de compilación formidable hormiga, que tiene todas las modificaciones para el apoyo multi-dex: https://github.com/ruboto/ruboto-irb/blob/master/build.xml

    Espero que eso ayude.

    He presentado el soporte multi-dex en la aplicación usando hormigas basadas en build.xml de Ruboto (felicitaciones a ellos!). Así que ese es el camino que yo recomendaría tomar. En ese caso usted no tiene que crear jarras de hormigas personalizadas, simplemente edite su build.xml y aplique todos los pasos de no-gradle desde https://developer.android.com/tools/building/multidex.html

    En cuanto a build.xml que tendría que echar un vistazo si todos los caminos juegan bien con los demás, es posible que haya cambiado algunos de ellos y la necesidad de ajustar los pasos de la hormiga.

    En mi caso tuve que generar también una lista de clases que deben ser colocadas en el primer dex generado (por defecto), por lo que la aplicación podría incluso iniciarse ya que carga dexes adicionales en la aplicación onCreate. Para hacer eso debes ejecutar script mainDexClasses, que encontrarás en tu build-tools:

    mainDexClasses [--output <output file>] <application path> 

    Alex Lipov escribió un buen mensaje al respecto.

    Cuando tiene lista de clases generadas, tiene que apuntar a esta lista en el parámetro de dx añadiendo

     <arg value="--main-dex-list="class_list_path" /> 

    Donde se ejecuta dx.

    Vale la pena recordar que el guión de Ruboto asume que sólo se creará un dex adicional, lo que no siempre ocurre. Tuve dos dexes adicionales generados por lo que implementó añadir si existe:

      <if> <condition> <available file="${third_dex_path}"/> </condition> <then> <exec executable="${aapt}" dir="${out.absolute.dir}" failonerror="true"> <arg line='add -v "${out.absolute.dir}/${resource.package.file.name}" ${third_dex}'/> </exec> </then> </if> 

    ¡Y el soporte multidex en Ant está encendido! No es tan fácil como con gradle, pero es posible.

    UPDATE: mi build.xml (aparte de las partes no relacionadas con la respuesta):

     <!-- Packages the application. Overriden in order to add "-post-package-resources"" dependency--> <target name="-package" depends="-dex, -package-resources, -post-package-resources"> <!-- only package apk if *not* a library project --> <do-only-if-not-library elseText="Library project: do not package apk..." > <if condition="${build.is.instrumented}"> <then> <package-helper> <extra-jars> <!-- Injected from external file --> <jarfile path="${emma.dir}/emma_device.jar" /> </extra-jars> </package-helper> </then> <else> <package-helper /> </else> </if> </do-only-if-not-library> </target> <target name="-post-package-resources"> <property name="second_dex" value="classes2.dex" /> <property name="third_dex" value="classes3.dex" /> <property name="second_dex_path" value="${out.absolute.dir}/${second_dex}" /> <if> <condition> <and> <available file="${second_dex_path}" /> <or> <not> <uptodate srcfile="${second_dex_path}" targetfile="${out.absolute.dir}/${resource.package.file.name}" /> </not> <uptodate srcfile="${out.absolute.dir}/${resource.package.file.name}" targetfile="${out.absolute.dir}/${resource.package.file.name}.d" /> </or> </and> </condition> <then> <echo>Adding classes2.dex to ${resource.package.file.name}</echo> <exec executable="${aapt}" dir="${out.absolute.dir}" failonerror="true"> <arg line='add -v "${out.absolute.dir}/${resource.package.file.name}" ${second_dex}'/> </exec> <if> <condition> <available file="${out.absolute.dir}/classes3.dex"/> </condition> <then> <echo>Adding classes3.dex to ${resource.package.file.name}</echo> <exec executable="${aapt}" dir="${out.absolute.dir}" failonerror="true"> <arg line='add -v "${out.absolute.dir}/${resource.package.file.name}" ${third_dex}'/> </exec> </then> </if> </then> </if> </target> <!-- builds dex in regular way and if it fails, switches to multidex--> <macrodef name="dex-helper"> <element name="external-libs" optional="yes" /> <attribute name="nolocals" default="false" /> <sequential> <condition property="verbose.option" value="--verbose" else=""> <istrue value="${verbose}" /> </condition> <condition property="jumbo.option" value="--force-jumbo" else=""> <istrue value="${dex.force.jumbo}" /> </condition> <!-- Regular DEX process. We would prefer to use the Android SDK ANT target, but we need to detect the "use multidex" error. https://android.googlesource.com/platform/sdk/+/tools_r21.1/anttasks/src/com/android/ant/DexExecTask.java --> <mapper id="pre-dex-mapper" type="glob" from="libs/*.jar" to="bin/dexedLibs/*-dexed.jar"/> <apply executable="${dx}" failonerror="true" parallel="false" dest="${out.dexed.absolute.dir}" relative="true"> <arg value="--dex" /> <arg value="--output" /> <targetfile/> <arg line="${jumbo.option}" /> <arg line="${verbose.option}" /> <fileset dir="." includes="libs/*" /> <external-libs /> <mapper refid="pre-dex-mapper"/> </apply> <apply executable="${dx}" resultproperty="dex.merge.result" outputproperty="dex.merge.output" parallel="true"> <arg value="--dex" /> <arg value="--output=${intermediate.dex.file}" /> <arg line="${jumbo.option}" /> <arg line="${verbose.option}" /> <path path="${out.dex.input.absolute.dir}"/> <path refid="out.dex.jar.input.ref" /> <external-libs /> </apply> <if> <condition> <or> <contains string="${dex.merge.output}" substring="Too many field references"/> <contains string="${dex.merge.output}" substring="Too many method references"/> </or> </condition> <then> <echo message="Number of field or method references is too big. Switching to multi-dex build." /> <multi-dex-helper> <external-libs> <external-libs/> </external-libs> </multi-dex-helper> </then> <else> <echo message="${dex.merge.output}"/> <fail status="${dex.merge.result}"> <condition> <not> <equals arg1="${dex.merge.result}" arg2="0"/> </not> </condition> </fail> </else> </if> </sequential> </macrodef> <macrodef name="multi-dex-helper"> <element name="external-libs" optional="yes" /> <sequential> <property name="mainDexClasses" location="${android.build.tools.dir}/mainDexClasses" /> <exec executable="${mainDexClasses}" failonerror="true" > <arg line="--output ${out.absolute.dir}/classes_to_kepp_in_main_dex"/> <arg file="${out.absolute.dir}/proguard/obfuscated.jar"/> </exec> <echo>Converting compiled files and external libraries into ${out.absolute.dir} (multi-dex)</echo> <echo>Dexing ${out.classes.absolute.dir} and ${toString:out.dex.jar.input.ref}</echo> <apply executable="${dx}" failonerror="true" parallel="true"> <arg value="--dex" /> <arg value="--multi-dex" /> <arg value="--main-dex-list=${out.absolute.dir}/classes_to_kepp_in_main_dex" /> <arg value="--output=${out.absolute.dir}" /> <arg line="${jumbo.option}" /> <arg line="${verbose.option}" /> <arg path="${out.absolute.dir}/proguard/obfuscated.jar" /> <path refid="out.dex.jar.input.ref" /> <external-libs /> </apply> </sequential> </macrodef> 

    También he añadido multidex a la construcción de ANT, pero de una manera ligeramente diferente que es más flexible en el soporte * .dex clases.

    En cualquier caso, lo hice en dos fases: 1) obtener DX a salida multidex entonces 2) utilizar aapt para incluir las clases multidex. Existe un paso opcional para producir la lista de clases principales al final de esta publicación, pero no es necesario para la implementación básica.

    Esta es la implementación en build.xml, con comentarios a seguir:

     ... your build script ... <!-- version-tag: custom --> <!-- (1) Override -package target to add additional JAR to the apk --> <property name="multidex-secondary-classes.jar" value="classes-secondary.jar" /> <target name="-package" depends="-dex, -package-resources, -create-multidex-secondary-classes-jar"> <!-- Copied from SDK/tools/ant/build.xml --> <!-- only package apk if *not* a library project --> <do-only-if-not-library elseText="Library project: do not package apk..." > <if condition="${build.is.instrumented}"> <then> <package-helper> <extra-jars> <!-- Injected from external file --> <jarfile path="${emma.dir}/emma_device.jar" /> </extra-jars> </package-helper> </then> <else> <!-- We can finesse apkbuilder by putting secondary classes file(s) in a jar file --> <if condition="${build.is.multidex}"> <then> <package-helper> <extra-jars> <jarfile path="${out.dir}/${multidex-secondary-classes.jar}" /> </extra-jars> </package-helper> </then> <else> <package-helper> <extra-jars /> </package-helper> </else> </if> </else> </if> </do-only-if-not-library> </target> <!-- (2) Create a JAR file of the secondary classes*.dex files --> <target name="-create-multidex-secondary-classes-jar" if="${build.is.multidex}"> <jar destfile="${out.dir}/${multidex-secondary-classes.jar}" basedir="${out.dir}" includes="classes*.dex" excludes="classes.dex" filesonly="true" /> </target> <!-- Standard import of Android build.xml --> <import file="${sdk.dir}/tools/ant/build.xml" /> <!-- (3) Replacement of "dex-helper" to support multidex --> <macrodef name="dex-helper"> <element name="external-libs" optional="yes" /> <attribute name="nolocals" default="false" /> <sequential> <!-- sets the primary input for dex. If a pre-dex task sets it to something else this has no effect --> <property name="out.dex.input.absolute.dir" value="${out.classes.absolute.dir}" /> <!-- set the secondary dx input: the project (and library) jar files If a pre-dex task sets it to something else this has no effect --> <if> <condition> <isreference refid="out.dex.jar.input.ref" /> </condition> <else> <path id="out.dex.jar.input.ref"> <path refid="project.all.jars.path" /> </path> </else> </if> <if condition="${build.is.multidex}" > <then> <if condition="${dex.force.jumbo}" > <else> <fail message="The following assumes dex.force.jumbo is true" /> </else> </if> <apply executable="${dx}" failonerror="true" parallel="true"> <arg value="--dex" /> <arg value="--force-jumbo" /> <!-- Specify a multi-dex APK --> <arg value="--multi-dex" /> <!-- For multidex output to a folder --> <arg value="--output" /> <arg value="${out.dir}" /> <path path="${out.dex.input.absolute.dir}" /> </apply> </then> <else> <!-- The value from SDK/tools/ant/build.xml --> <dex executable="${dx}" output="${intermediate.dex.file}" dexedlibs="${out.dexed.absolute.dir}" nolocals="@{nolocals}" forceJumbo="${dex.force.jumbo}" disableDexMerger="${dex.disable.merger}" verbose="${verbose}"> <path path="${out.dex.input.absolute.dir}"/> <path refid="out.dex.jar.input.ref" /> <external-libs /> </dex> </else> </if> </sequential> </macrodef> 

    La modificación (1) reemplaza el destino del paquete para permitir que los tarros adicionales se pasen a ApkBuilder. Resulta que ApkBuilder acaba de copiar el contenido de los archivos JAR en el APK, así que si ponemos nuestras clases [1-N] .dex en un JAR, podemos obtener ApkBuilder para empaquetar esos frascos adicionales en el APK.

    (2) Crea ese archivo JAR adicional con todas las clases * .dex excepto classes.dex, por lo que admite cualquier número de archivos extras.de.dex

    (3) Es un trabajo alrededor para ese hecho que el "dex" AntTask no apoya ninguna manera de pasar – multi-dex a "dx.bat", que sabe utilizarlo. Miré lo que dx.bat estaba invocando y agregó que directamente aquí (Enmendado: michaelbrz tiene una mejor aplicación en su guión, así que lo tomé prestado.

    BTW, siempre ejecuta Proguard (ya sea ofuscada o no obscurecida) para obtener la clase y el método de contracción. Esto es útil para generar una lista de "clases principales". En nuestro caso, hemos añadido Google Play Services y hemos estropeado nuestro límite de método de archivo DEX, por lo que decidimos poner esas clases y métodos en el dex secundario. Generar esa lista de clases de la salida de Proguard es una simple cuestión de filtrar dump.txt:

     <property name="multidex-main-dex-list.file" value="bin/multidex-main-dex-list.txt" /> <target name="-create-multidex-main-list" if="${build.is.multidex}"> <delete file="${multidex-main-dex-list.file}" /> <copy file="${out.dir}/proguard/dump.txt" tofile="${multidex-main-dex-list.file}" > <filterchain> <!-- Convert classes to the right format --> <tokenfilter> <containsregex pattern="^..Program class..(.*)" replace="\1.class"/> </tokenfilter> <!-- Exclude Google Play Services --> <linecontains negate="true"> <contains value="com/google/android/gms/"/> </linecontains> </filterchain> </copy> </target> 
    FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.