跳到主要内容

解决 ANT 打包 65536 问题

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

图片:Appendix_DexMerger_65536

解决这个问题有两种方法:

  • 通过 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 分组打包的流程,对方法进行分组打包。

简单梳理后,可知处理流程是:

  1. Ant 工程分组
    分为分组1,分组2,...

  2. 手动合并分组 2,分组 3,和其他组为 dex2,dex3 等。

  3. 分组1放入 Unity 中

  4. Unity 打包输出 APK

  5. 将 dex2,dex3,等合到 APK 中。

  6. 将 APK 重签名。

合并流程

注意

ANT 工程不只包括了 Java 方法,还包括了 AndroidManifest.xml 文件和 资源 res 文件,需要让 Unity 打包到 APK 中。

INTLUtils 提供的功能包括:

  1. 将每个 Ant 工程的 JAR 包合并为 DEX 文件

  2. 将每个 Ant 工程的 AndroidManifest 合并

  3. 将每个 Ant 工程的资源 res 文件合并

为了将 AndroidManifest.xml 文件和资源 res 文件合并,整理后的主要流程是:

  1. Ant 工程分组分为分组1,分组2,...

  2. 将除分组1 以外的分组放在一起,合并 AndroidManifest.xml 和 res 文件,并将合并的文件复制到分组 1。

  3. 分组2、分组3、… 手动合并为 dex2,dex3,...

  4. 分组1放入 Unity 中

  5. Unity 打包输出 APK

  6. 将 dex2,dex3,等合到 APK 中。

  7. 将 APK 重签名

操作指引

拷贝需要合并的包到 INTLUtils 目录

图片:Appendix_DexMerger_copy_Android

需要注意:

  1. 需要配置主 AndroidManifest.xml 文件。

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

    图片:Appendix_DexMerger_AndroidManifest

    图片:Appendix_DexMerger_AndroidManifest_File

  2. 需要将外层 JAR 包拷贝到 libs 目录下。

    libs 目录作为 Unity 的主要 JAR 目录,将随分组1 进行打包。

    需要将 multidex-1.0.3.jar 文件拷贝到 libs 目录下,multidex 务必要放在 dex1 中。

    图片:Appendix_DexMerger_libs

Ant 分组

将外层 AndroidManifest.xml 文件和 libs 目录需要放置分组1中,其他工程基本可以认为任意分组,分组的原则可以简单按 4 MB (不包括 .so 文档的大小,因此有些库一个 .so 就几兆,.so 是不会影响 Java 方法数的) 一个分组进行划分。保证一个 DEX 中不会超过 65536 个方法数。

分组数量根据方法数量进行划分。

图片:Appendix_DexMerger_dex2

合并分支1以外的 AndroidManifest 和资源

  1. 将除分组1以外的分组(分组2、分组3、...)放在一起。

  2. 将分组1中的 AndroidManifest.xml 文件拷贝到分支 1 以外的分组中(主要用于将其他分组的 AndroidMainfest.xml 写入到主 AndroidManifest.xml),并创建 libs 文件夹。

    图片:Appendix_DexMerger_2-5

  3. 通过 INTLUtils 进行 AndroidManifest.xml 和资源合并。

    java -jar INTLUtils.jar -unityRoot ./2-5 -buildTools /Users/lucasfan/Library/Android/sdk/build-tools/28.0.3 -output ./output2-5

    图片:Appendix_DexMerger_output2-5

  4. 将生成的 AndroidManifest.xml 和 res 拷贝到分组 1中。

    图片:Appendix_DexMerger_new1

分别生成 dex2,dex3,...

  1. 在每个分组增加一个空的 AndroidManifest.xml 和 libs 文件夹。

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

  2. 通过 INTLUtils 进行 Java 方法合并。

    java -jar INTLUtils.jar -unityRoot ./2 -buildTools /Users/lucasfan/Library/Android/sdk/build-tools/28.0.3 -output ./output2

图片:Appendix_DexMerger_unity

将分组1放回 Unity 中

图片: Appendix DexMerger Unity

Unity 打包为 Android APK

图片:Appendix_DexMerger_Split

将 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 编译器。