Flutter 应用混淆(包体积优化实践)

大规模应用开发过程中,性能管理是重中之重,其中就包括启动速度,交互等多个方面的优化,应用包大小和渲染性能也是很重要的衡量指标。当我们提到性能时,也无外乎运行时间占用空间这两个话题。

本篇文章,我们主要探讨的就是如何解决应用程序包大小的问题

应用程序大小

应用的大小是开发者和用户都能够直接感知的指标。作为开发者,我们清楚地知道 APK 文件、App Bundle 或者 IPA 之类的安装包中包含了应用代码、资源文件以及运行时的所有内容,如果体积过大,则对性能影响会造成非常大的影响,不仅会占用更多空间,也可能会无法使用一些特色功能,如 Android instant apps。

默认情况下,为了支持热重载和代码级调试等功能,debug 版本的安装包会天然具有较大体积,性能优化时,可以忽略这种情况。

在 Android Studio 中,打开任意 Flutter 项目,使用 flutter build apk --release 构建安装包之后,点击 Build >> Analyze Apk,会发现 lib 目录的大小占整个包大小的 95% 以上,并且其中默认编译出来的 APK 包含有 android-arm,android-arm64,android-x64 三种二进制文件。

因此,各大 Android 应用商店的或者 App Store 接收到包之后还会做进一步处理,如根据用户的机型过滤掉一些 native 库、DPI 等。官方也推荐我们使用 flutter build appbundle 构建 Android App Bundle(.aab) 文件,具体请看 https://developer.android.com/platform/technology/app-bundle。

对于不支持自动拆解 App Bundles 的厂商,我们可以直接使用 flutter build apk --split-per-abi 直接编译出各平台下的拆分 APK 包:

  • <app dir>/build/app/outputs/apk/release/app-armeabi-v7a-release.apk
  • <app dir>/build/app/outputs/apk/release/app-arm64-v8a-release.apk
  • <app dir>/build/app/outputs/apk/release/app-x86_64-release.apk

下面,我们也可以使用最新的开发工具检查和估算 Flutter 应用程序的具体大小…

优化包体积

Flutter 1.22 之后,官方发布了一款应用包大小的分析工具,可以帮助开发者直观地看到应用各个模块占用空间大小的详细信息。我们只需要在构建应用时传入 --analyze-size 参数即可使用该分析工具:

  • flutter build apk --analyze-size
  • flutter build appbundle --analyze-size
  • flutter build ios --analyze-size
  • flutter build linux --analyze-size
  • flutter build macos --analyze-size
  • flutter build windows --analyze-size

如果构建的是 Android APK 或 bundle,则还需要指定目标平台架构,可以如下这些命令:

1
2
3
4
5
flutter build apk --target-platform android-arm --analyze-size 
flutter build apk --target-platform android-arm64 --analyze-size
flutter build apk --target-platform android-x64 --analyze-sizeflutter build appbundle --target-platform android-arm --analyze-size
flutter build appbundle --target-platform android-arm64 --analyze-size
flutter build appbundle --target-platform android-x64 --analyze-size

运行结果如下:

该命令主要有两个作用:

  1. 在编译 Dart 代码时会记录 Dart packages 中代码大小的使用情况。
  2. 输出应用大小相关的具体细节,并将结果最终保存在 *-code-size-analysis_*.json 文件中供我们使用 DevTools 做进行分析。

如上图,终端中已经展示了项目中每项所占的空间大小,也可以通过 json 文件,使用 Dart DevTools 工具继续做更深入的分析(https://flutter.dev/docs/development/tools/devtools/overview)。

Example breakdown of app in DevTools

应用混淆

代码混淆 (Obfuscated code) 亦称花指令,是将计算机程序的代码,转换成一种功能上等价,但是难于阅读和理解的形式的方式。通常针对的是二进制文件。实际使用中将可以代码中的各种元素,如变量,函数,类的名字改写成无意义的名字,防止攻击者会应用逆向。

——维基百科

要混淆 Flutter 应用程序,首先需要适配不同平台的版本:

android /iOS:Flutter 1.16.2 以后支持。

macOS:macOS(Flutter 1.13 发布测试版)也支持在 Flutter 1.16.2 版本后混淆。

Linux / Windows:不支持

Flutter Web:不支持

本篇文章,我们主要介绍如何混淆 Dart 代码,关于减少应用大小,我们也可以尝试如下几种方法:

  • 删除未使用的资源(Resource Shrinking)。

  • 压缩从三方库导入的资源。

  • 混淆原生代码(java):

    修改 <app dir>/android/app/build.gradle 文件:

    1
    2
    3
    4
    5
    6
    7
    buildTypes {
    release {
    minifyEnabled true // add this
    proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' // add this
    signingConfig signingConfigs.release // this is default for release
    }
    }

    创建 proguard-rules.pro 文件:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    ## Flutter wrapper
    -keep class io.flutter.app.** { *; }
    -keep class io.flutter.plugin.** { *; }
    -keep class io.flutter.util.** { *; }
    -keep class io.flutter.view.** { *; }
    -keep class io.flutter.** { *; }
    -keep class io.flutter.plugins.** { *; }
    # -keep class com.google.firebase.** { *; } // uncomment this if you are using firebase in the project
    -dontwarn io.flutter.embedding.**
    -ignorewarnings

    `gradle.properties:

    1
    extra-gen-snapshot-options=--obfuscate
  • 压缩 PNG 和 JPEG 等资源文件。

  • 删除不必要的第三方库。

  • 使用 .svg 格式的图标。

Flutter 应用混淆

Flutter 应用的混淆非常简单,只需要在构建 release 版应用时结合使用 --obfuscate --split-debug-info 这两个参数即可。

--obfuscate --split-debug-info 用来指定输出调试文件的位置,该命令会生成一个符号映射表。目前支持 apk,appbundle,ios 和 ios-framework 等目标平台(macOS 和 aar 在 master 和 dev 分支中支持),如下:

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

混淆成功后,需要保存符号映射表,以便以后需要去混淆跟踪代码堆栈。

相关命令的其他信息,可以运行 flutter build apk -h 查看,如果不支持该命令,核实 Flutter 版本,执行 flutter upgrade更新。

读取混淆堆栈

要调试混淆后的应用,可以执行以下两个步骤:

  1. 找到符号映射表文件。如在 Android arm64 下发生 crash,分析 app.android-arm64.symbols 文件。
  2. 运行 flutter symbolize 命令,并指定堆栈跟踪的文件和符号映射表文件:
1
flutter symbolize -i <stack trace file> -d /out/android/app.android-arm64.symbols

关于 symbolize 命令的详细信息,可以运行 flutter symbolize -h 查看。

下一步

使用 Flutter 支持的代码混淆功能后,一定会在程度上减少应用程序的总大小,但这仅仅是应用优化中一个小小的环节,有关 Flutter 应用运行效率、UI 渲染相关的更多优化问题将会在之后的文章逐一探讨。

延伸阅读

Measuring your app’s size

Obfuscating The Flutter App

Reducing Flutter App Size

Build and release an Android app

Obfuscating dart code

关注公众号「Meandni」,及时阅读最新前沿技术动态,不至于落后时代。

扫一扫,关注「Meandni」
文章作者: Joker
文章链接: https://meandni.com/2020/11/04/obfuscating-the-flutter-app/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Joker's Blog