使用Custom Main Gradle减少DEX 64K

解读

在国内 Unity 项目上线 Android 渠道时,DEX 64K 方法数限制是必现痛点:当应用+引擎+第三方 SDK 的方法数超过 65536,打包直接失败。Unity 2019 以后官方把主 Gradle 模板暴露出来,允许开发者用 Custom Main Gradle TemplateAssets/Plugins/Android/mainTemplate.gradle)直接干预最终合并的 build.gradle,从而按需裁剪、延迟加载、分包(multidex)或启用 R8/ProGuard,把方法数压到安全线以下,而不必等出包后再手动改 Android Studio 工程。面试官问“怎么用 Custom Main Gradle 减少 DEX 64K”,核心是想确认两点:

  1. 你是否真的理解 DEX 64K 的根因(方法数≠字节数,而是 Dalvik 64K 引用槽位);
  2. 能否在 Unity 侧闭环解决,而不是把锅甩给 Android 同事。

知识点

  1. 64K 触发条件
    • dx 打包时所有 classes*.dex 索引总和 ≥ 65536;
    • 国内渠道普遍要求 minSdkVersion ≤ 20,不能默认 multidex,必须手动开启。
  2. Custom Main Gradle Template 生效路径
    • Unity 出 Gradle 工程时,把 mainTemplate.gradle 直接复制为 launcher/build.gradle
    • baseProjectTemplate.gradlelauncherTemplate.gradlegradleTemplate.properties 共同构成最终构建脚本。
  3. 关键 DSL
    • multidexEnabled true
    • implementation 'androidx.multidex:multidex:2.0.1'
    • proguardFiles, minifyEnabled, shrinkResources
    • packagingOptions 剔除多余 .so 与重复 META-INF
  4. Unity 侧辅助
    • Player Settings → Publishing Settings → Custom Main Gradle Template 勾选;
    • Player Settings → Target Architecture 只保留 arm64,可瞬间砍掉 30% 方法数;
    • Managed Stripping Level 选 Medium 或 High,配合 link.xml 保留反射类;
    • 使用 R8(Gradle 6+ 默认)而非 ProGuard,压缩率提升 10~15%。
  5. 国内渠道特殊坑
    • 华为、OPPO 安全扫描不允许 multidexlegacy 版本,必须 androidx.multidex
    • 部分 SDK(推送、支付)自带 android.support 旧库,需 exclude module 防止重复合并;
    • 若接 抖音 SDK,其 bytecode 插桩会膨胀方法数,需要 pickFirsts 强制去重。

答案

  1. 在 Unity Editor 中勾选 Player Settings → Publishing Settings → Custom Main Gradle Template,生成 Assets/Plugins/Android/mainTemplate.gradle
  2. 打开模板,在 android 节点内增加:
    defaultConfig {
        minSdkVersion **19**        // 国内渠道最低
        targetSdkVersion **33**
        multiDexEnabled **true**    // 关键开关
    }
    
  3. dependencies 区块追加:
    implementation 'androidx.multidex:multidex:2.0.1'
    
  4. 启用 R8 压缩:
    buildTypes {
        release {
            minifyEnabled **true**
            shrinkResources **true**
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-unity.txt', 'proguard-user.txt'
        }
    }
    
    并在 Assets/Plugins/Android 新建 proguard-user.txt,把 Unity 反射类、热更 DLL、第三方 SDK 的 keep 规则写全,防止运行时 ClassNotFound。
  5. 裁剪架构:
    ndk {
        abiFilters 'arm64-v8a'      // 仅保留 64 位,so 方法数大幅下降
    }
    
  6. 若方法数仍逼近 64K,则在 launcherTemplate.gradle 中把 dexOptionspreDexLibraries = false,强制 主 dex 最少化,并把 非启动路径的代码 通过 multidex.keep 文件延后到 secondary dex,确保冷启动主 dex 方法数 < 65536。
  7. 出包前用 Build Analyzerdexcount-gradle-plugin 扫描:
    ./gradlew assembleRelease -PcountDexMethods
    
    确认 release 主 dex 方法数 < 60K,留 5K 冗余给渠道二次加固插桩。

通过以上步骤,可在 Unity 侧闭环完成 DEX 64K 治理,无需出包后再回 Android Studio 补救,符合国内快节奏发版要求。

拓展思考

  1. Root-DEX 与 Secondary-DEX 加载时机
    国内加固厂商(腾讯乐固、网易易盾)会在 attachBaseContext 后重新注入 multidex,若 Unity 的 Application 继承关系写死,容易 NoClassDefFoundError。最佳实践是 自定义 Application 继承 MultiDexApplication,并在 onCreate 最前端初始化热更框架,保证次级 dex 加载完成后再反射 Unity 主 Activity。
  2. Instant Run 与 D8 冲突
    部分 CI 仍用旧 Gradle 4.x,开启 Instant Run 会导致 dxd8 混合编译,方法数统计失真。升级 Gradle 7+ 并关闭 Instant Run,可让 R8 全量优化,方法数再降 5~8%
  3. Unity 2022 LTS 的新方案
    Unity 2022.3 开始支持 Assembly Definition Reference + Build-time code stripping,可把巨量引擎模块(UI、Tilemap、Analytics)在导出 Gradle 前就裁剪掉,主 dex 方法数直接减半;但国内老项目多数基于 2019 LTS,需要评估升级成本。
  4. 数字孪生/仿真场景
    若项目非游戏而是 数字孪生,往往集成 GIS、CAD、BIM 解析库,方法数爆炸更猛。此时可在 mainTemplate.gradle 里用 productFlavors拆分 APK
    • phone flavor 只保留核心可视化,方法数 < 50K;
    • pad flavor 打开全量模块,强制 multidex;
      通过 Unity Cloud Build 自动打双包,兼顾 华为应用商店私有部署 两种场景。