解决 ANT 打包 65536 问题
通过 Unity 的 ANT 打包时,由于每个 DEX 包的大小有限制 (每个 DEX 最多支持的方法数为 65536),可能导致在打包时出现报错。更多详情,请参见 为方法数超过 64K 的应用启用 MultiDex。

解决这个问题有两种方法:
- 通过 Gradle 打包,并为
minSdkVersion低于 21 的应用开启 MultiDex 选项,详见 针对 MultiDex 配置应用。 - 手动合并 JAR 包和资源文件,并导入
multidex.jar。
minSdkVersion 为 21 或更高版本的应用默认启用 MultiDex,无需开启 MultiDex 选项,详见 Android 5.0 及更高版本的 MultiDex 支持。
无需开启 MultiDex 时请在 AndroidManifest.xml 中移除 android.multidex.MultiDexApplication。
由于低版本的 Unity 不支持 Gradle 打包,只能通过 ANT 打包,因此只能手动合并 JAR 包和资源。但目前 Google 官网未提供相应的工具,为了解决这个问题,Player Network SDK 提供了INTLUtils 工具来进行 JAR 包和资源文件的打包。
手动合并原理
在 Android 编译过程中,会将所有的 Java 方法写入 DEX 文件中,如果不开启 MultiDex,那么 APK 中就只有一个 classes.dex。
在开启 MultiDex 后,Gradle 打包会将 Java 方法进行分组,将重要 (启动时需要) 的 Java 方法分组到 dex1 (在启动过程中需要的方法) 中 并将其他 Java 方法放置在 dex2 (classes2.dex),dex3 (classes3.dex),等。
手动合并的原理就是模拟 Gradle 分组打包的流程,对方法进行分组打包。
简单梳理后,可知处理流程是:
-
Ant 工程分组
分为分组1,分组2,... -
手动合并分组 2,分组 3,和其他组为 dex2,dex3 等。
-
分组1放入 Unity 中
-
Unity 打包输出 APK
-
将 dex2,dex3,等合到 APK 中。
-
将 APK 重签名。
合并流程
ANT 工程不只包括了 Java 方法,还包括了 AndroidManifest.xml 文件和 资源 res 文件,需要让 Unity 打包到 APK 中。
INTLUtils 提供的功能包括:
-
将每个 Ant 工程的 JAR 包合并为 DEX 文件
-
将每个 Ant 工程的 AndroidManifest 合并
-
将每个 Ant 工程的资源 res 文件合并
为了将 AndroidManifest.xml 文件和资源 res 文件合并,整理后的主要流程是:
-
Ant 工程分组分为分组1,分组2,...
-
将除分组1 以外的分组放在一起,合并 AndroidManifest.xml 和 res 文件,并将合并的文件复制到分组 1。
-
分组2、分组3、… 手动合并为 dex2,dex3,...
-
分组1放入 Unity 中
-
Unity 打包输出 APK
-
将 dex2,dex3,等合到 APK 中。
-
将 APK 重签名
操作指引
拷贝需要合并的包到 INTLUtils 目录

需要注意:
-
需要配置主
AndroidManifest.xml文件。主 AndroidManifest.xml 文件中的 manifest 属性需要包括属性
xmlns:tools="http://schemas.android.com/tools",且package改为测试 demo 的包名。

-
需要将外层 JAR 包拷贝到 libs 目录下。
libs 目录作为 Unity 的主要 JAR 目录,将随分组1 进行打包。
需要将
multidex-1.0.3.jar文件拷贝到 libs 目录下,multidex 务必要放在 dex1 中。
Ant 分组
将外层 AndroidManifest.xml 文件和 libs 目录需要放置分组1中,其他工程基本可以认为任意分组,分组的原则可以简单按 4 MB (不包括 .so 文档的大小,因此有些库一个 .so 就几兆,.so 是不会影响 Java 方法数的) 一个分组进行划分。保证一个 DEX 中不会超过 65536 个方法数。
分组数量根据方法数量进行划分。

合并分支1以外的 AndroidManifest 和资源
-
将除分组1以外的分组(分组2、分组3、...)放在一起。
-
将分组1中的
AndroidManifest.xml文件拷贝到分支 1 以外的分组中(主要用于将其他分组的AndroidMainfest.xml写入到主AndroidManifest.xml),并创建 libs 文件夹。
-
通过 INTLUtils 进行
AndroidManifest.xml和资源合并。java -jar INTLUtils.jar -unityRoot ./2-5 -buildTools /Users/lucasfan/Library/Android/sdk/build-tools/28.0.3 -output ./output2-5
-
将生成的
AndroidManifest.xml和 res 拷贝到分组 1中。
分别生成 dex2,dex3,...
-
在每个分组增加一个空的
AndroidManifest.xml和 libs 文件夹。AndroidManifest.xml文件中的 manifest 属性需要包括属性xmlns:tools="http://schemas.android.com/tools",且package改为测试 demo 的包名。 -
通过 INTLUtils 进行 Java 方法合并。
java -jar INTLUtils.jar -unityRoot ./2 -buildTools /Users/lucasfan/Library/Android/sdk/build-tools/28.0.3 -output ./output2

将分组1放回 Unity 中

Unity 打包为 Android APK

将 dex2,dex3,... 合到 APK 中
工具生成的 DEX 文件名称均为 classes2.dex,需要根据第几个 dex,具体修改为 classesX.dex。
使用 aapt 将 dex 文件合并到 APK 中
aapt a test.apk classes2.dex
必须要将 APK 和 DEX 文件放到同级目录,这样才能保证将 DEX 文件添加到 APK 的根目录下。
将 APK 重签名
apksigner sign --ks debug.keystore --ks-key-alias XXXX --ks-pass pass:XXXX --key-pass pass:XXXX -out test-signed.apk test.apk
注意,如果密码中有(!)等特殊字符,需加上\,例如:--ks-pass pass:\!1234。
INTLUtils 参数说明
-
-mainProject,主工程目录,如果设置了unityRoot,可不设置。 -
-subProject,子工程 (目录),多个工程用空格隔开。如果设置了unityRoot,无需设置。 -
-buildTools,Android build tools 目录 (必须)。 -
-output,输出目录 (可选)。 -
-package,目标 package 名称 (可选)。默认为主工程 package name。 -
-unityRoot,标准 Android Unity 工程目录 (可选)。设置该选项相当于同时设置了-mainProject和-subProject。 -
-useD8,是否使用 D8 编译器 (可选)。默认使用 DX 编译器。开启时需要设置为1。 -
-androidJar,Android SDK 的
android.jar的路径,使用 D8 编译器时必须。路径为:android_sdk/platforms/api-level/android.jar。
常见问题
依赖 JDK1.8 的 JAR 生成 DEX 失败
使用 DX 编译器编译 JAR 生成 DEX 时将提示类似如下错误:
: invalid opcode ba - invokedynamic requires --min-sdk-version >= 26 (currently 13)
原因是 DX 不支持 JDK1.8 的 JAR 转为 DEX,需要使用 D8 编译器。
将 useD8 设置为 1,并设置 androidJar。可开启 D8 编译器。