Flutter 集成排查与 APK 瘦身问题解决

一、起点:一个 173MB 的 Android APK

工程是一个 Android 主工程,集成了同级目录的 Flutter module,外加两个本地子模块。ac flavor 的 release APK 重达 173 MB,远超合理范围。


二、排查过程(这部分是关键,比改动本身更值得记)

  1. 先体检 Flutter 集成方式

settings.gradle.kts 里看到:

· 一行硬编码的绝对路径引用 --- 只能在某台特定机器上跑

· 存在冗余的模块声明

· app/build.gradle.kts 里的注释与实际代码不一致,造成误导

  1. 第一次 clean release 构建:173 MB

体积异常大,开始定位。

  1. 拆 APK,找最胖的文件

unzip -l app-ac-release.apk → 按 lib/arm64-v8a/ 大小排序

· libflutter.so = 137.63 MB(正常应该 ~7--10 MB)

· libapp.so = 4.5 MB(Dart AOT 产物,正常)

  1. 排除 ABI 多打的可能

确认了 abiFilters 只保留 arm64-v8a 生效,APK 里只有一个 ABI 目录。问题锁定在单一 .so 文件本身。

  1. 检查 .so 的二进制属性

file libflutter.so → "with debug_info, not stripped"

带完整 DWARF 调试符号,未 strip。

  1. 验证手动 strip 可行

用 NDK 的 llvm-strip --strip-unneeded:138 MB → 11 MB。确认问题不在 .so 本身,而在构建流水线没自动 strip 它。

  1. 第一个错误假设:构建模式配置是元凶

在 Flutter module 的配置文件中看到某行配置,推测它强制了非 release 模式。删掉后重打 ------ 大小没变。假设证伪。后续确认打进 APK 的就是 release 引擎的原始未 strip 版本。

  1. 用 --info 跑 strip 任务找到铁证

    Unable to strip library '.../libflutter.so' due to missing strip tool for ABI 'arm64-v8a'. Packaging it as is.

真因找到: 主工程模块没声明 ndkVersion,AGP 找不到 llvm-strip,对所有 prebuilt jniLibs(包括 libflutter.solibapp.so 等)的 strip 静默跳过。Flutter 子模块虽然有 ndkVersion,但 strip 发生在主工程打包阶段,按主工程的配置走。

  1. 修复:加一行 ndkVersion,重打验证

173 MB → 46 MB,省了 127 MB。


三、最终改动清单(4 个文件)

  1. settings.gradle.kts

改动: 把硬编码绝对路径替换为通过 gradle.properties 配置的相对路径,加存在性校验,删冗余声明。

kotlin 复制代码
// 改前:硬编码绝对路径 + 冗余 include
// 改后:
val flutterModulePath = (settings.extra.properties["flutter.module.path"] as? String)
    ?: "../FlutterModule"
val flutterIncludeScript = File(rootDir, "$flutterModulePath/.android/include_flutter.groovy")
require(flutterIncludeScript.exists()) {
    "Flutter module not found. 请在 gradle.properties 设置 flutter.module.path"
}
apply(from = flutterIncludeScript)

收益: 其他开发者 / CI 不再受限于特定机器路径;新成员只需把 Flutter module 放同级目录即可,或在 ~/.gradle/gradle.properties 覆盖路径。

  1. gradle.properties

改动: 新增 Flutter module 路径属性(默认相对路径)。

复制代码
flutter.module.path=../FlutterModule
  1. app/build.gradle.kts

改动 1: android {} 顶部加 ndkVersion。

kotlin 复制代码
android {
    ndkVersion = "28.2.13676358"  // 让 AGP 能找到 llvm-strip
    // ... 其他配置
}

改动 2: 移除误导性注释。

  1. Flutter module 的 .android/local.properties

改动: 删除非必要的构建模式配置行,让 Flutter 自然跟随 host 的 build type。


四、量化结果

指标 改前 改后 变化

APK 总体积 173 MB 46 MB −127 MB (−73%)

libflutter.so 137.63 MB 10.59 MB −127 MB (−92%)

libapp.so 4.50 MB 4.50 MB 持平

Clean 构建耗时 1m 33s 1m 09s 略快(少写 100+ MB IO)


五、后续可继续优化

  1. R8 + 资源压缩

在 release 构建类型中启用 isMinifyEnabled = true 和 isShrinkResources = true,配合 ProGuard 规则,可再砍掉 5--15 MB 的 Java/Kotlin 代码与无用资源。

  1. 改用 AAB 上架

Google Play 支持按设备配置切片下发,用户实际下载体积会进一步减小。

  1. 资源审计

扫描 res/raw、assets/ 目录,检查是否有冗余字体、动画、视频等大文件未使用。

  1. 图片资源优化

将 PNG 图片转为 WebP 格式,或使用矢量图替代位图。

  1. Flutter 产物进一步瘦身

· 启用 Flutter 的 --split-debug-info 选项分离调试符号

· 使用 --obfuscate 混淆 Dart 代码


六、教训 / 经验沉淀

· APK 异常大时,先 unzip -l 看分布,比盲目猜测高效得多

· Flutter 引擎 .so 原始 ~138 MB 是正常的,关键看是否被 strip

· ndkVersion 不只是给 native 代码模块用的 --- 只要工程有 prebuilt jniLibs(哪怕来自 Flutter 这类第三方),AGP 都需要它来定位 strip 工具,否则会静默跳过

· AGP 的 strip 失败信息默认不会出现在普通构建日志里,要加 --info 才能看到 "Unable to strip ... missing strip tool"

· 不要轻信可疑配置 --- 验证比假设重要,本次第一个猜测就被验证证伪了

相关推荐
KKei16381 小时前
Flutter for OpenHarmony学术论文管理APP技术文章
flutter·华为·harmonyos
程序员老刘·15 小时前
Perry能取代Flutter吗?跨平台的三种技术路线
flutter·跨平台开发·客户端开发
西西学代码21 小时前
Flutter---侧边栏
flutter
xmdy58661 天前
Flutter+开源鸿蒙实战|企业级工具APP Day2 全局网络封装与 Dio 拦截器实战(鸿蒙兼容版)
flutter·开源·harmonyos
xmdy58661 天前
Flutter+开源鸿蒙实战:企业级工具类APP开发教程(含第三方库适配)
flutter·开源·harmonyos
Swift社区1 天前
Flutter / React / ArkUI:在鸿蒙 PC 上怎么选?
flutter·react.js·harmonyos
恋猫de小郭1 天前
Android Studio 放着没怎么用,怎么也会越来越卡?
android·前端·flutter
xmdy58662 天前
Flutter + 开源鸿蒙跨端实战|基于空间地理信息的**城市全域智慧泊车调度与多维运维管理平台** Day1 项目架构基座与工程化环境搭建
flutter·开源·harmonyos
KillerNoBlood2 天前
2026移动端跨平台开发面经总结
android·算法·flutter·ios·移动开发·鸿蒙·kmp