24

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

 3 years ago
source link: https://segmentfault.com/a/1190000023163171
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

追逐,探索,永不停歇~

ErMrmqn.gif

前言

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

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

UVbe6zr.png!web

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

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

强烈要求优化 Apk 大小。

raii22v.png!web

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

探索之路 一部曲

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

buueueq.png!web

雷同使用下面的命令(默认带有 --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%:

FFjiayu.png!web

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

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

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

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

viQNnm6.png!web

这里看到将我们编写的 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 内容:

QzaEJrQ.png!web

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

zuqeqm2.png!web

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

还记得 Android 混淆的魅力吗?

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

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

  • flutter build apk --obfuscate --split-debug-info=/<project-name>/<directory>

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

  • --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).

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

MzqIV3i.png!web

相比一开始的 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=</out/android/app.arm64.symbols>                              A path to the symbols file generated with "--split-debug-info".
-i, --input=</crashes/stack_trace.err>                                         A file path containing a Dart stack trace.
-o, --output=<A file path for a symbolicated stack trace to be written to.>    

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK