解决 ANT 打包 65536 问题
如果通过 Unity 的 ANT 打包时,由于每个 dex 包的大小有限制 (每个 dex 最多支持的方法数为 65536),所以导致打包时出现方法数超过 65536 的报错。进一步了解这个问题,请参见 Google 网站。
解决这个问题有两种方法
- 通过 Gradle 打包,并开启 MultiDex 选项,具体请参见 针对 MultiDex 配置应用。
- 手动合并 JAR 包和资源文件,并导入
multidex.jar
。
由于低版本的 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
文件拷贝到分支 1 以外的分组中 (主要用于将其他分组的AndroidMainfest
写入到主AndroidManifest
),并创建 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
和 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 编译器。