gradle ビルド システムで multi-dex オプションを有効にするのは簡単ですが、ant ビルドでこのオプションを有効にする方法の例は見つかりませんでした。これをどのようにアーカイブできますか?
3 に答える
2 つのオプションがあります。
- DexExecTask [マルチ dex 用の新しいパラメーターを導入] を変更し、ant.jar をコンパイルし、この jar をビルドに使用します。このオプションは好きではありません。すべてのチーム メンバーに最新の ant.jar を提供する必要があるからです。
- プロジェクトの build.xml ファイルを変更します。マルチデックスをサポートするためのすべての変更を含む素晴らしいantビルドファイルを見つけました: https://github.com/ruboto/ruboto-irb/blob/master/build.xml
それが役立つことを願っています。
Ruboto の build.xmlに基づいた ant を使用して、アプリケーションに multi-dex サポートを導入しました (彼らに敬意を表します!)。というわけで、私がお勧めする道です。その場合、カスタム ant jar を作成する必要はありません。build.xml を編集して、 https: //developer.android.com/tools/building/multidex.html からすべての非 gradle ステップを適用するだけです。
build.xml については、すべてのパスが互いにうまく機能するかどうかを確認する必要があります。それらの一部を変更した可能性があり、ant ステップを調整する必要があります。
私の場合、最初に生成された dex (デフォルト) に配置する必要があるクラスのリストも生成する必要があったため、アプリケーションの onCreate で追加の dex をロードするときにアプリを起動することもできました。そのためには、ビルドツールにあるスクリプト mainDexClasses を実行する必要があります。
mainDexClasses [--output <output file>] <application path>
Alex Lipovはそれについて素晴らしい記事を書きました。
クラスのリストを生成したら、dx のパラメーターでこのリストを指定する必要があります。
<arg value="--main-dex-list="class_list_path" />
dx が実行される場所。
Ruboto のスクリプトは、追加の dex が 1 つだけ作成されることを前提としていますが、常にそうであるとは限りません。2 つの追加の dex が生成されたので、存在する場合は追加を実装しました。
<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>
Ant での multidex のサポートもオンになっています。Gradleほど簡単ではありませんが、可能です。
更新:私のbuild.xml(回答に関連しない部分を除く):
<!-- 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>
multidex も ANT ビルドに追加しましたが、class*.dex サポートでより柔軟な、少し異なる方法で追加しました。
いずれにせよ、これを 2 つのフェーズで行いました。1) DX を取得して multidex を出力し、次に 2) aapt を使用して multidex クラスを含めます。この投稿の最後にメイン クラスのリストを作成するためのオプションの手順がありますが、基本的な実装には必要ありません。
以下は build.xml での実装で、コメントが続きます。
... 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>
変更 (1) は -package ターゲットを置き換えて、追加の jar を ApkBuilder に渡すことができるようにします。ApkBuilder は JAR ファイルの内容を APK にコピーするだけであることがわかります。そのため、classes[1-N].dex を JAR に入れると、ApkBuilder を使用してこれらの余分な jar を APK にパッケージ化できます。
(2) classes.dex を除くすべての classes*.dex を含む追加の JAR ファイルを構築するため、任意の数の追加の classes.dex ファイルをサポートします。
(3) 「dex」AntTask が --multi-dex を「dx.bat」に渡す方法をサポートしていないという事実に対する回避策です。私は dx.bat が呼び出しているものを見て、それをここに直接追加しました (修正: michaelbrz は彼のスクリプトにより優れた実装を持っているので、それを借りました。ありがとう)
ところで、クラスとメソッドの縮小を取得するために、常に Proguard (難読化または非難読化のいずれか) を実行します。これは、「メイン クラス」リストを生成するのに便利です。私たちの場合、Google Play Services を追加したことで、DEX ファイル メソッドの制限がなくなったため、これらのクラスとメソッドをセカンダリ dex に配置することにしました。Proguard の出力からそのクラス リストを生成するには、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>