ant脚本使用multidex解决65536问题

xiaoxiao2021-02-28  119

现在的android项目应该大多都用gradle构建了吧,但是仍然有很多老项目使用的ant工具,这里并不推荐使用ant构建,因为最新的android sdk tools里边已经去掉了ant相关的lib包。不管gradle也好,ant也好,其实编译打包apk的过程基本都是一样的。

我遇到的这个项目,经历了两次方案的调整。

方案一:

最开始并没用dx的multidex参数,而是将所有的第三方jar包(应用启动时用的除外)打到从包中去,从包可以是多个,剩下的源码打到主包中,也就是分别调用dx打出dex包。

<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> <echo>Converting external libraries into ${assets}/${dex}...</echo> <delete file="${asset.absolute.dir}\plug.jar"/> <delete file="${asset.absolute.dir}\plug2.jar"/> <if condition="${proguard.enabled}"> <then> <separete inputjar="${out.dex.input.absolute.dir}" mapping="${obfuscate.absolute.dir}/mapping.txt" alljarpath="project.all.jars.path" plugjarconfig="${plug.jar.config}" plugjarconfig2="${plug.jar.config2}" plugjarpath="project.plug.path" plugjarpath2="project.plug.path2" projectjarpath="project.core.path"/> <path id="project.dex.core.path"> <path refid="project.core.path" /> </path> </then> <else> <filterlib alljarpath="project.all.jars.path" plugjarconfig="${plug.jar.config}" plugjarconfig2="${plug.jar.config2}" plugjarpath="project.plug.path" plugjarpath2="project.plug.path2" projectjarpath="project.core.path"/> <path id="project.dex.core.path"> <path path="${out.dex.input.absolute.dir}"/> <path refid="project.core.path" /> </path> </else> </if> <dex executable="${dx}" output="${asset.absolute.dir}\plug.jar" dexedlibs="${out.dexed.absolute.dir}" nolocals="@{nolocals}" forceJumbo="${dex.force.jumbo}" disableDexMerger="${dex.disable.merger}" verbose="${verbose}"> <path refid="project.plug.path" /> </dex> <dex executable="${dx}" output="${asset.absolute.dir}\plug2.jar" dexedlibs="${out.dexed.absolute.dir}" nolocals="@{nolocals}" forceJumbo="${dex.force.jumbo}" disableDexMerger="${dex.disable.merger}" verbose="${verbose}"> <path refid="project.plug.path2" /> </dex> <delete file="${asset.absolute.dir}\plug.jar.d"/> <delete file="${asset.absolute.dir}\plug2.jar.d"/> <checksum file="${asset.absolute.dir}\plug.jar" forceOverwrite="yes"/> <checksum file="${asset.absolute.dir}\plug2.jar" forceOverwrite="yes"/> <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 refid="project.dex.core.path" /> <external-libs /> </dex> </sequential> </macrodef> 其中separete和filterlib是两个自定义的task,作用都是从project.all.jars.path中过滤出事先定义好的第三方jar包的path,区别是separete是在代码经过混淆后执行,将混淆后的代码生成plug.jar,可以看到调用了三次dex。

代码中加载从包的方法:

PathClassLoader pathClassLoader = (PathClassLoader) app.getClassLoader(); DexClassLoader dexClassLoader = new DexClassLoader(libPath, app.getDir("dex", 0).getAbsolutePath(), libPath, app.getClassLoader()); InjectResult result = null; try { Object dexElements = combineArray(getDexElements(getPathList(pathClassLoader)), getDexElements(getPathList(dexClassLoader))); Object pathList = getPathList(pathClassLoader); setField(pathList, pathList.getClass(), "dexElements", dexElements); } catch (IllegalArgumentException e) { result = makeInjectResult(false, e); e.printStackTrace(); } DexClassLoader加载从包dex,然后将pathClassLoader与dexClassLoader中的DexPathList的dexElements属性值合并,再放到pathClassLoader中的DexPathList中。最开始方案一是满足需求的,但随着主包越来越大,终于还是65536了,这种只分出去第三方jar包的做法还是不太灵活,于是有了方案二。

方案二:

使用dx的multidex参数,指定maindexlist清单文件,在这个清单文件中的类会打到主包中,清单文件的生成可以使用build-tools下的mainDexClasses脚本生成。

<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}" /> <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> <echo message="start dx ${maindexlist.dir}"/> <multidex executable="${dx}" output="${out.dir}" dexedlibs="${out.dexed.absolute.dir}" nolocals="@{nolocals}" forceJumbo="${dex.force.jumbo}" disableDexMerger="${dex.disable.merger}" multidex="true" mainDexList="${maindexlist.dir}" verbose="${verbose}"> <path path="${out.dex.input.absolute.dir}"/> <path refid="out.dex.jar.input.ref" /> <external-libs /> </multidex> </sequential> </macrodef> <target name="-maindexlist" depends="-set-release-mode, -compile, -post-compile, -obfuscate"> <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> <maindexlist output="build/maindexlist-proguard.txt" mainDexList="build/maindexlist.txt" mappingFile="${out.absolute.dir}/proguard/mapping.txt" > <path path="${out.dex.input.absolute.dir}"/> <path refid="out.dex.jar.input.ref" /> </maindexlist> </target> m ultidex和maindexlist也是两个自定义task,multidex其实就是调用的dx --dex --multi-dex --main-dex-list=maindexlist.txt  --minimal-main-dex --output bin --input-list,maindexlist task的作用是生成混淆后的主包清单文件。应用启动时代码原理和方案一差不多,也可以直接使用官方的android-surpport-multidex.jar包

转载请注明原文地址: https://www.6miu.com/read-51084.html

最新回复(0)