Flutter Notes|Flutter-Apk 大小优化探索

追逐,探索,永不停歇~

前言

还记得刚入坑 Flutter 打包时,被深深震惊了一番,卧槽,这包好大!

  • ✓ Built build/app/outputs/apk/release/app-release.apk (23.8MB).

足足将近 24 MB,第一反应真的懵逼了。

当然直接提交市场后,也是被人各种 diss,原因还是没什么功能,包贼大,用户下载贼不舒服。

强烈要求优化 Apk 大小。

既然是探索,前提我还是个刚入 Flutter 坑的小白白,所以嘛,难免不全面,欢迎各位大佬拍砖、指点~

探索之路 一部曲

首先,我首次打包的方式如下:

雷同使用下面的命令(默认带有 –release):

  • flutter build apk

一、熊猫压缩法(减少 0.7 MB)

首先第一想法,图片我没做压缩,同样经过查看后,发现图片在 apk 占比为 4.1% :

  • 2.3 % Flutter 引用到的资源文件;
  • 1.8% Android 启动页的背景图。

最后,我们通过国宝之手试试最后能减少多少?

这里分别针对 Flutter 下图片资源、Android/iOS 启动页进行压缩。

再次运行 build apk 后,完成输出如下日志:

  • ✓ Built build/app/outputs/apk/release/app-release.apk (23.1MB).

再来看 Apk 中图片的占比以及降低到 1%:

最终 Apk 大小直接减少了 0.7 MB,还是比较爽的。

二、so 优化大法(减少 14 MB)

做 Android 的小伙伴知道,对于我们这些小厂没能力搞动态下发 so 的小渣渣而言,只能默默逆向大厂 Apk,看看人家是怎么做的,然后借(抄)鉴(袭)。

针对 Flutter 打出的 Apk 包,排在第一位的便是 lib,占比 86.4%,足足有 19.6 MB:

这里看到将我们编写的 Dart 代码转化为不同架构下的 so 库,以供原生调用(我是这么猜测的哈)。

针对不同 CPU 架构所代表含义,尤其 Flutter 打包 Apk 生成的三种 CPU 架构分别对应什么含义:

  • x86_64: Intel 64 位,一般用于平板或者模拟器,支持 x86 以及 x86_64 CPU 架构设备。
  • arm64-v8a: 第 8 代 64 位,包含 AArch32、AArch64 两个执行状态,且对应 32 、64 bit,并且支持 armeabi、armeabi-v7a 以及 arm64-v8a。
  • armeabi-v7a: 第 7 代 arm v7,使用硬件浮点运算,具有高级拓展功能,兼容 armeabi 以及 armeabi-v7a,而且目前大部分手机都是这个架构。

其实我们第一次通过 flutter build apk 命令生成 apk 时,Google 这里已经为我们提示了:

heliquan@Mac  ~/CodePro/FlutterPro/xxx_app master ● flutter build apk
You are building a fat APK that includes binaries for android-arm, android-arm64, android-x64.
If you are deploying the app to the Play Store, it's recommended to use app bundles or split the APK to reduce the APK size.
    To generate an app bundle, run:
        flutter build appbundle --target-platform android-arm,android-arm64,android-x64
        Learn more on: https://developer.android.com/guide/app-bundle
    To split the APKs per ABI, run:
        flutter build apk --target-platform android-arm,android-arm64,android-x64 --split-per-abi
        Learn more on:  https://developer.android.com/studio/build/configure-apk-splits#configure-abi-split                                        
Running Gradle task 'assembleRelease'...                                
Running Gradle task 'assembleRelease'... Done                      13.8s
✓ Built build/app/outputs/apk/release/app-release.apk (23.1MB).

接下来通过以下命令进行分别打包(构建指定 CPU 架构类型 Apk 包):

  • flutter build apk –target-platform android-arm,android-arm64,android-x64 –split-per-abi

这里解释下这个命令的含义:

  • 首先 flutter build apk 表示当前构建 release 包;
  • 后面 android-arm,android-arm64,android-x64 则是指定生成对应架构的 release 包;
  • 最后的 –split-per-abi 则表示告知需要按照我们指定的类型分别打包,如果移除则直接构建包含所有 CPU 架构的 Apk 包。

所以这个命令的含义就是告诉编译器,我需要你为我针对我指定的三种不同架构分别生成对应的 Apk 包。

有的小伙伴就说了,你这空口无凭,没证据啊。

好,我给你运行一波~

  • 验证:flutter build apk –target-platform android-arm,android-arm64,android-x64 结果
heliquan@Mac  ~/CodePro/FlutterPro/xxx_app   master ●  flutter build apk --target-platform android-arm,android-arm64,android-x64                
You are building a fat APK that includes binaries for android-arm, android-arm64, android-x64.
If you are deploying the app to the Play Store, it's recommended to use app bundles or split the APK to reduce the APK size.
    To generate an app bundle, run:
        flutter build appbundle --target-platform android-arm,android-arm64,android-x64
        Learn more on: https://developer.android.com/guide/app-bundle
    To split the APKs per ABI, run:
        flutter build apk --target-platform android-arm,android-arm64,android-x64 --split-per-abi
        Learn more on:  https://developer.android.com/studio/build/configure-apk-splits#configure-abi-split                                        
Removed unused resources: Binary resource data reduced from 817KB to 815KB: Removed 0%
Running Gradle task 'assembleRelease'...                                
Running Gradle task 'assembleRelease'... Done                     115.8s
✓ Built build/app/outputs/apk/release/app-release.apk (23.1MB).

看见没,事实论证结果。

最后,我们采取告知编译器为我们生成指定 CPU 架构的 Apk 的方式,并查看对应输出日志信息:

heliquan@Mac  ~/CodePro/FlutterPro/xxx_app   master ●  flutter build apk --target-platform android-arm,android-arm64,android-x64 --split-per-abi                                         
Removed unused resources: Binary resource data reduced from 816KB to 814KB: Removed 0%
Removed unused resources: Binary resource data reduced from 816KB to 814KB: Removed 0%
Removed unused resources: Binary resource data reduced from 816KB to 814KB: Removed 0%
Running Gradle task 'assembleRelease'...                                
Running Gradle task 'assembleRelease'... Done                      36.0s
✓ Built build/app/outputs/apk/release/app-armeabi-v7a-release.apk (9.8MB).
✓ Built build/app/outputs/apk/release/app-arm64-v8a-release.apk (10.1MB).
✓ Built build/app/outputs/apk/release/app-x86_64-release.apk (10.2MB).

看看 app-armeabi-v7a-release.apk 包大小,结果是不是贼喜人?由 23.8 MB 直接减少到 9.8 MB。

随后我们看下对应的 apk 内容:

lib 占比也从原来的 86.4%,19.6 MB 直接减少为 67.2%,大小 6.3 MB。

三、混淆大法好(减少 0.4 MB)

还记得 Android 混淆的魅力吗?

  • 增加逆向难度;
  • 减少 Apk 大小;
  • 。。。

对此 Flutter 也为我们提供了混淆命令:

  • flutter build apk –obfuscate –split-debug-info=//

简单说下我个人对于此命令的理解:

  • –obfuscate:开启混淆操作;
  • –split-debug-info=:将因混淆生成的 map 符号表缓存到此位置。

这里我们先测试下,直接构建完整包,并添加混淆操作,输出的 apk 大小有多少:

heliquan@Mac  ~/CodePro/FlutterPro/xxx_app   master ●  flutter build apk --obfuscate --split-debug-info=HLQ_Struggle
You are building a fat APK that includes binaries for android-arm, android-arm64, android-x64.
If you are deploying the app to the Play Store, it's recommended to use app bundles or split the APK to reduce the APK size.
    To generate an app bundle, run:
        flutter build appbundle --target-platform android-arm,android-arm64,android-x64
        Learn more on: https://developer.android.com/guide/app-bundle
    To split the APKs per ABI, run:
        flutter build apk --target-platform android-arm,android-arm64,android-x64 --split-per-abi
        Learn more on:  https://developer.android.com/studio/build/configure-apk-splits#configure-abi-split                                        
Running Gradle task 'assembleRelease'...                                
Running Gradle task 'assembleRelease'... Done                      60.3s
✓ Built build/app/outputs/apk/release/app-release.apk (21.9MB).

同样也在项目根目录下生成了符号文件:

相比一开始的 23.8 MB,减少了 1.9 MB。那么我们直接针对不同 CPU 生成对应的 Apk 并添加混淆结果又是怎样呢?

➜  xxx_app git:(master) ✗ flutter build apk --obfuscate --split-debug-info=debugInfo  --target-platform android-arm,android-arm64,android-x64 --split-per-abi                                       
Running Gradle task 'assembleRelease'...                                
Running Gradle task 'assembleRelease'... Done                      39.3s
✓ Built build/app/outputs/apk/release/app-armeabi-v7a-release.apk (9.4MB).
✓ Built build/app/outputs/apk/release/app-arm64-v8a-release.apk (9.7MB).
✓ Built build/app/outputs/apk/release/app-x86_64-release.apk (9.8MB).

未混淆的 v7a 大小与开启混淆相比,开启混淆减少了 0.4 MB。

还不错。

对于混淆的文件,出问题怎么调试呢?

莫慌,Flutter 同样提供了 symbolize 神器,当然这个不在涉猎范围内,就不详细解释了,知道就好:

heliquan@Mac  ~/CodePro/FlutterPro/haozhuan_app   master ●  flutter symbolize -h
Symbolize a stack trace from an AOT compiled flutter application.

Usage: flutter symbolize [arguments]
-h, --help                                                                     Print this usage information.
-d, --debug-info=                              A path to the symbols file generated with "--split-debug-info".
-i, --input=                                         A file path containing a Dart stack trace.
-o, --output=    

Run "flutter help" to see global options.

End

上面叨叨半天,总结一个比较有用的命令:

  • flutter build apk –obfuscate –split-debug-info=HLQ_Struggle –target-platform android-arm,android-arm64,android-x64 –split-per-abi

含义就是,哥,帮我针对不同 CPU 架构分别打包,别忘记混淆哈,生成的符号表文件记得帮我放在 HLQ_Struggle 目录下。

详细日志如下:

heliquan@Mac  ~/CodePro/FlutterPro/xxx_app   master ●   flutter build apk --obfuscate --split-debug-info=HLQ_Struggle --target-platform android-arm,android-arm64,android-x64 --split-per-abi                                          
Removed unused resources: Binary resource data reduced from 816KB to 814KB: Removed 0%
Removed unused resources: Binary resource data reduced from 816KB to 814KB: Removed 0%
Removed unused resources: Binary resource data reduced from 816KB to 814KB: Removed 0%
Running Gradle task 'assembleRelease'...                                
Running Gradle task 'assembleRelease'... Done                      36.9s
✓ Built build/app/outputs/apk/release/app-armeabi-v7a-release.apk (9.4MB).
✓ Built build/app/outputs/apk/release/app-arm64-v8a-release.apk (9.7MB).
✓ Built build/app/outputs/apk/release/app-x86_64-release.apk (9.8MB).

当然也有小伙伴说了,打包前 clean 下,生成的包会小,实际测试一下:

heliquan@Mac  ~/CodePro/FlutterPro/xxx_app   master ●  flutter clean           
Cleaning Xcode workspace...                                         3.3s
Deleting build...                                                2,774ms (!)
Deleting .dart_tool...                                              41ms
Deleting Generated.xcconfig...                                       0ms
Deleting flutter_export_environment.sh...                            0ms
Deleting App.framework...                                            9ms
 heliquan@Mac  ~/CodePro/FlutterPro/xxx_app   master ●   flutter build apk --obfuscate --split-debug-info=HLQ_Struggle --target-platform android-arm,android-arm64,android-x64 --split-per-abi                                          
Running Gradle task 'assembleRelease'...                                                                                                                                                  Removed unused resources: Binary resource data reduced from 816KB to 814KB: Removed 0%                             
Removed unused resources: Binary resource data reduced from 816KB to 814KB: Removed 0%                             
Removed unused resources: Binary resource data reduced from 816KB to 814KB: Removed 0%                             
Running Gradle task 'assembleRelease'...                                                                           
Running Gradle task 'assembleRelease'... Done                     215.3s (!)
✓ Built build/app/outputs/apk/release/app-armeabi-v7a-release.apk (9.4MB).
✓ Built build/app/outputs/apk/release/app-arm64-v8a-release.apk (9.7MB).
✓ Built build/app/outputs/apk/release/app-x86_64-release.apk (9.8MB).

根据以上输出结果,并没发现减少了哪儿。

一点小经历分享,当然肯定会有更好的操作方法,但是目前仅次于此,欢迎各位大佬交流~

Thanks