Flutter Android 包体积优化:ABI 拆分 + R8 混淆实战
背景
Flutter 项目打 Android Release 包时,APK 体积往往远大于预期。一个通用 APK 动辄几十上百 MB,对分发(蒲公英/侧载)和用户下载体验都不友好。
本文记录一套组合优化方案,目标是 把实际分发给用户的安装包做小、做专。
现象
典型的包体积问题表现为:
- 单个 APK 同时包含
armeabi-v7a、arm64-v8a、x86_64等多套 ABI 的 .so 库 - 未使用的代码和资源原样打进 Release 包
- 调试/测试用架构也被带进正式分发包
优化方案
核心思路是 四个组合拳:
| 步骤 | 作用 | 实施位置 |
|---|---|---|
| 1. R8 混淆 + 资源裁剪 | 移除无用代码和资源 | build.gradle |
| 2. 限制目标 ABI | 去掉 x86_64(模拟器才用) | build.gradle |
3. --split-per-abi 拆包 |
按 CPU 架构打独立 APK | 构建命令 |
4. 分发优先选 arm64-v8a |
主力用户包 | 发布脚本 |
1. Release 启用 R8 混淆和资源裁剪
在 android/app/build.gradle 的 release 构建类型中:
1 | // android/app/build.gradle |
这一步的效果:
- 移除未使用的 Java/Kotlin 代码
- 裁掉无用的 drawable、layout 等资源
- 对第三方 SDK 也有效(只要它们的 ProGuard 规则允许)
2. 限制 Release 目标 ABI
默认情况下 Flutter 会把所有 ABI 的 .so 库都打进 APK。实际上:
| ABI | 适用场景 | Release 是否需要 |
|---|---|---|
arm64-v8a |
绝大多数 Android 真机(64位ARM) | ✅ 主力 |
armeabi-v7a |
老旧 32 位 ARM 设备 | ✅ 备用 |
x86_64 |
模拟器 | ❌ 去掉 |
在 android/app/build.gradle 中覆盖 ABI 过滤:
1 | // android/app/build.gradle.kts |
注意:这里用
afterEvaluate而不是直接在defaultConfig里写,是因为 Flutter Gradle 插件在 apply 阶段可能会覆盖 ABI 配置。afterEvaluate确保在插件完成后覆盖,不会互相打架。
3. Release 构建改为按 ABI 拆包
构建命令加上 --split-per-abi:
1 | flutter build apk --release --split-per-abi |
输出目录 build/app/outputs/flutter-apk/ 会生成三个独立的 APK:
1 | app-armeabi-v7a-release.apk |
比一个「大而全」的通用包,单个 APK 体积显著缩小。用户下载时只需要对应架构的包。
4. 分发时优先选 arm64-v8a
发布脚本中,Android APK 的选取逻辑:
1 | # 优先取 arm64-v8a(主流真机) |
原因:
- 现在市面上绝大多数 Android 真机都是 64 位 ARM
arm64-v8a是实际分发的主力包x86_64只给模拟器和特殊测试环境
完整构建脚本
1 |
|
如果上架应用商店:优先 AAB
对 Google Play 等应用商店分发,最佳实践是打 AAB:
1 | flutter build appbundle --release |
商店会根据设备 ABI 自动分发对应版本,不需要人工拆包和挑包。
验证
本地构建验证:
1 | flutter build apk --release --split-per-abi && \ |
预期结果:
- 看到按 ABI 拆分的独立 APK
- 单个 APK 比不拆分前明显更小
- 构建日志中包含
--split-per-abi
注意事项
.so库才是体积大头:如果项目依赖了 IM、音视频、WebView 等原生 SDK,so 体积本身就不小。当前方案解决的是”构建方式导致的额外膨胀”,不是把原生依赖变没。修改 ABI 要理解 Flutter Gradle 插件行为:不要在不理解插件行为的前提下随意同时改
abiFilters和--split-per-abi,否则可能某些 ABI 不生成或行为冲突。后续还能做的:检查是否有未使用但仍保留的原生插件、调试资源是否误入 Release、图片资源是否可以继续压缩。








