加入收藏 | 设为首页 |

道光通宝-浅谈 Flutter 构建 | 开发者说·DTalk

海外新闻 时间: 浏览:294 次

本文原作者: Leo,原文发布于掘金: https://juejin.im/post/5d68fb1af265da03d063b69e

不同版别构建产品的差异

写这篇文章的时刻为: 2019.08.25,当时 Flutter SDK 最新版别为 v1.9.5,其间 stable 最新版别为 v1.7.8+hotfix.4。

跟着版别的更新,Flutter 构建的产品也在调整,以 Android 为例,咱们运用默许的 flutter_app 项目来测验,在不修正源码的情况下,运用不同的 SDK 版别来履行打包指令,每个版别都打两个包: debug 和 release。

每个版别生成的 Flutter 产品如下所示,这儿不列出 fonts、LICENSE 这些文件:

从上面的表格来看,1.5.x 版别和 1.7.x 版别的改变是比较显着的,首要的改变有两点:

榜首,1.7.x 版别增加了支撑一同编译 32 位和 64 位两种架构,这个在之前一向被诟病,现在官方支撑了。

第二,release 方法下,不运用 snapshot 文件,而运用 libapp.so。snapshot 是 Dart VM 所支撑的一种文件格局,相似 JVM 的 jar 相同,能够运转在虚拟机环境的文件。

除了改变以外,咱们还留意到一些不变的文件,比方 debug 方法下,一向存在的 kernel_blob.bin 文件;lib 目录下 libflutter.so 文件;这些文件各自的效果又是什么呢。带着疑问,咱们一同看看 Flutter 的构建进程。

Flutter 支撑的编译方法

关于编译方法这块首要参阅的:

  • Flutter’s Compilation Patterns

关于编译方法这块首要参阅的:

  • Flutter’s Compilation Patterns

Flutter’s Compilation Patterns

Flutter 是根据 Dart 开发的,所以 Flutter 的构建跟 Dart 是分不开的。所以,咱们先讲 Dart。

在讲 Dart 之前,咱们先了解两种编译方法,JIT 和 AOT:

JITJIT (Just In Time) 翻译为即时编译,指的是在程序运转中,将热门代码编译成机器码,进步运转功率。常见比方有 V8 引擎和 JVM,JIT 能够充分利用解说型言语的长处,动态履行源码,而不必考虑渠道差异性。这儿需求留意的是,关于 JVM 来说,源码指字节码,而不是 Java 源码。

这儿需求差异,JIT 和解说型言语的差异,JIT 是一种编译方法,比方,Java 是编译型言语,但它也能够运用 JIT。

这儿需求差异,JIT 和解说型言语的差异,JIT 是一种编译方法,比方,Java 是编译型言语,但它也能够运用 JIT。

AOT

AOT (Ahead Of Time) 称为运转前编译,指的是在程序运转之前,现已编译成对应渠道的机器码,不需求在运转中解说编译,就能够直接运转。常见比方有 C 和 C++。

尽管,咱们会差异 JIT 和 AOT 两种编译方法,但实践上,有许多言语并不是彻底运用 JIT 或许 AOT 的,一般它们会混用这两种方法,来到达最大的功能优化。

Dart 支撑的编译方法

Dart VM 支撑四种编译方法:

1, :最常见的 JIT 方法,能够直接在虚拟机中履行 Dart 源码,像解说型言语相同运用。经过履行dart xxx.dart就能够运转,写一些暂时脚本,十分便利。

2, Kernel Snapshots: JIT 方法,和 方法不同的是,这种方法履行的是 Kernel AST的二进制数据,这儿不包括解析后的类和函数,编译后的代码,所以它们能够在不同的渠道之间移植。

在《Flutter’s Compilation Patterns》(https://proandroiddev.com/flutters-compilation-patterns-24e139d14177) 这篇文章中,将 Kernel Snapshots 称为 Snapshots 也是对的,这应该是之前的叫法,从 dart-lang 的 wiki 中能够看出来:

Dart Kernel 是 Dart 程序中的一种中心言语,更多的材料可阅览 Kernel Documentation(https://github.com/dart-lang/sdk/wiki/Kernel-Documentation)。

经过履行 dart --snapshot-kind=kernel --snapshot=xx.snapshot xx.dart生成。

3, JIT Application Snapshots:JIT 方法,这儿履行的是现已解析过的类和函数,所以它会运转起来会更快。可是这不是渠道无关,它只能针对 32 位或许 64 位架构运转。

经过履行 dart --snapshot-kind=app-jit --snapshot=xx.sna道光通宝-浅谈 Flutter 构建 | 开发者说·DTalkpshot xx.dart生成。

4, AOT Application Snapshots:AOT 方法,在这种方法下,Dart 源码会被提早编译成特定渠道的二进制文件。

要运用 AOT 方法,需求运用 dart2aot指令, 详细运用为: dart2aot xx.dart xx.dart.aot,然后运用 dartaotruntime指令履行。

Dart JIT 方法需求合作 Dart VM (虚拟机) ,AOT 则会运用 runtime 来履行,引证官网中的图片来阐明:

能够用下面这张图来总结上面的四种方法:

图片来源于 flutters-compilation-patterns

图片来源于 flutters-compilation-patterns

四种方法下的产品巨细和发动速度比较:

图片来源于 exploring-flutter-in-android

图片来源于 exploring-flutter-in-android

Flutter 构建进程

下面的剖析都是根据 v1.5.4-hotfix.2

下面的剖析都是根据 v1.5.4-hotfix.2

履行 flutter build apk时,默许会生成 release APK,实践是履行的 sdk/bin/flutter 指令,参数为 build apk:

上面的指令完好应该为: dart flutter_tools.snapshot args,snapshot 文件咱们上面提过,是 Dart 的一种代码调集,相似 Java 中 Jar 包。

flutter_tools 的源码坐落 sdk/packages/flutter_tools/bin 下的 flutter_tools.dart,跟 Java 相同,这儿也有个 main函数,其间调用的是 executable.main函数。

在 main 函数中,会注册多个指令处理器,比方:

  • DoctorCommand 对应 flutter doctor 指令
  • CleanCommand 对应 flutter clean 指令
  • 等等

咱们这非有必要研讨的方针是 BuildCommand,它里边又包括了以下子指令处理器:

  • BuildApkCommand 对应 flutter build apk 指令
  • BuildAppBundleCommand 对应 flutter build bundle 指令
  • BuildAotCommand 对应 flutter build aot 指令
  • 等等

Build APK

首要咱们要理清 build apk 或许 build ios,和 build bundle、build aot 之间的联系。这儿咱们以 build apk 为例:

当履行 flutter build apk时,终究会调用到 gradle.dart 中的 _buildGradleProjectV2办法,在这儿终究也是调用 gradle 履行 assemble task。而在项目中的 android/app/build.gradle 中能够看到:

也便是说,会在 flutter.gradle 这儿去刺进一些编译 Flutter 产品的脚本。

flutter.gradle 是经过插件的方法去完成的,这个插件命名为 FlutterPlugin。这个插件的首要效果有一些几点:

  • 除了默许的 debug 和 release 之外,新增了 profile、dynamicProfile、dynamicRelease 这三种 b道光通宝-浅谈 Flutter 构建 | 开发者说·DTalkuildTypes:
  • 动态增加 flutter.jar 依靠:

动态增加第三方插件依靠:

  • 在 assemble task 中增加一个 FlutterTask,这个 task 十分重要,这儿会去生成 Flutter 所需求的产品:

首要,当 buildType 是 profile 或 release 时,会履行 flutter build aot:

  • 道光通宝-浅谈 Flutter 构建 | 开发者说·DTalk

其次,履行 flutter build bundle指令,当 buildType 为 profile 或 release 时,增加额定的 --precompiled 选项。

略微总结下,当 buildType 为 debug 时,只需求履行:

而当 buildType 为 release 时,需求履行两个指令:

Build AOT

当履行 flutter build aot时,相关的逻辑在 BuildAotCommand 中,它首要分红两个过程:

1, 编译 kernel

kernel 指 Dart 的一种中心言语,更多材料能够阅览 Kernel-Documentation。终究会调用到 compile.dart 中的 KernelCompiler.compile办法:

engineDart 指向 Dart SDK 目录,上面代码履行的终究指令能够简化为:

app.dill 这儿面其实就包括了咱们的事务代码了,能够运用 strings app.dill 检查:

2, 生成 snpshot

相关的代码坐落 base/build.dart 中的 AOTSnapshotter.build函数中,有两种方法: app-aot-assemble 和 app-aot-blobs。

app-aot-assemble

在履行 aot 指令时,增加 --build-shared-library选项,完好指令如下:

flutter build aot --build-shared-library

iOS 只能运用这种方法,在 Flutter SDK 1.7.x 之后,这个也是 Android 的默许选项。

iOS 只能运用这种方法,在 Flutter SDK 1.7.x 之后,这个也是 Android 的默许选项。

这种方法下,会将产品编译为二进制文件,在 iOS 上为 App.framework,Android 上则为 app.so。

app-aot-blobs

当运用这种方法时,会生成四个产品,分别是:

in道光通宝-浅谈 Flutter 构建 | 开发者说·DTalkstr 全称是 Instructions

了解更多: Flutter-engine-operation-in-AOT-Mode (https://github.com/flutter/flutter/wiki/Flutter-engine-operation-in-AOT-Mode)

instr 全称是 Instructions

了解更多: Flutter-engine-operation-in-AOT-Mode (https://github.com/flutter/flutter/wiki/Flutter-engine-operation-in-AOT-Mode)

  • isolate_snapshot_data:

    表明 isolate 堆存储区的初始状况和特定的信息。和 vm_snapshot_data 合作,更快的发动 Dart VM。

  • isolate_snapshot_instr:

    包括由 Dart isolate 履行的 AOT 代码。

  • vm_snapshot_data:

    表明 isolates 之间的同享的 Dart 堆存储区的初始状况,用于更快的发动 Dart VM。

  • vm_snapshot_instr:

    包括 VM 中所有的 isolates 之间同享的常见例程的指令。

isolate_snapshot_data:

表明 isolate 堆存储区的初始状况和特定的信息。和 vm_snapshot_data 合作,更快的发动 Dart VM。

isolate_snapshot_instr:

包括由 Dart isolate 履行的 AOT 代码。

vm_snapshot_data:

表明 isolates 之间的同享的 Dart 堆存储区的初始状况,用于更快的发动 Dart VM。

vm_snapshot_instr:

包括 VM 中所有的 isolates 之间同享的常见例程的指令。

isolate_snapshot_data 和 isolate_snapshot_instr 跟事务相关,而 vm_snapshot_data 和 vm_snapshot_instr 则是跟 VM 相关,无关事务。

咱们能够运用 strings检查下 isolate_snapshot_data 中内容:

小结

上面两种方法相关的代码如下:

  • 闻喜刘福虹

iOS 默许运用 app-aot-assemble 方法,更多是 App Store 自身的约束:

> 引证于[flutters-compilation-patterns](https://proandroiddev.com/flutters-compilation-patterns-24e139d14177)>> App Store does notallow dispatch binary executable code.

而 Android 默许运用 app-aot-blobs 方法,或许更多是从功能方面考虑,有必要调用从 so 文件中调用 native 函数,需求用 JNI,有功能损耗,并且调用也比较费事,不过在 Flutter SDK 1.7.x 现已改成 app-aot-assemble 方法。#### Build Bundle

当履行 `flutter build bundle`时,相关的代码逻辑在 BuildBundleCommand 中,build bundle 首要做两件事:榜首,假如没有增加 `--precompiled`选项时,会先编译 kernel;第二,生成 assets(图片、字体等)。

1. 编译 kernel。这个过程和 build aot 的榜首个过程是相同的,终究会生成 app.dill,这是事务代码编译后的产品。一同,这个会创立一个 kernelContent,这个在第二个过程会讲到:

``` dartkernelContent = DevFSFileContent(fs.file(compilerOutput.outputFilename));```

2. 生成 assets。首要,会搜集图片资源、字体等:

``` dartfinal AssetBundle assets = await buildAssets( manifestPath: manifestPath, assetDirPath: assetDirPath, packagesPath: packagesPath, reportLicensedPackages: reportLicensedPackages, ); ```

其间包括有以下内容:

![assets](https://user-gold-cdn.xitu.io/2019/8/30/16ce1dca5d92427d?w=745&h=265&f=png&s=49891)

接着,会将这些,包括上面生成 kernelContent,一同搜集到指定目录,kernelContent 便是编译生成的 app.dill,但这儿会复制偏重命名为 kernel_blob.bin:

``` dartconst String _kKernelKey = 'kernel_blob.bin'; const String _kVMSnapshotData = 'vm_snapshot_data'; const String _kIsolateSnapshotData = 'isolate_snapshot_data'; final String vmSnapshotData = artifacts.getArtifactPath(Artifact.vmSnapshotData, mode: buildMode); final String isolateSnapshotData = artif道光通宝-浅谈 Flutter 构建 | 开发者说·DTalkacts.getArtifactPath(Artifact.isolateSnapshotData, mode: buildMode); assetEntries[_kKernelKey] = kernelContent; assetEntries[_kVMSnapshotData] = DevFSFileContent(fs.file(vmSnapshotData)); assetEntries[_kIsolateSnapshotData] = DevFSFileContent(fs.file(isolateSnapshotData)); ```

咱们留意到,这儿同样会生成 vm_snapshot_data 和 isolate_snapshot_data,但并没有生成相应的指令集。在这儿的 isolate_snapshot_data 中并不会包括咱们的事务代码,事务代码会存放在 kernel_blob.bin 文件中。能够运用 `strings`指令检查,并且这两个文件的 MD5 值是共同的。

```MD5 (app.dill) = 3876e8c6f4b13a88cc3cfc3b9fd108c4MD5 (flutter_assets/kernel_blob.bin) = 3876e8c6f4b13a88cc3cfc3b9fd108c4```关于 snapshot 生成相关能够去看 **gen_snapshot**。

#### 小结

##### debug

debug 方法下,会履行一个指令:

``` shellflutter build bundle

它会生成 assets、vm_snapshot_data、isolate_snapshot_data 和 kernel_bloc.bin。其间咱们的事务代码存放在 kernel_bloc.bin 中,它是 app.dill 的复制。

release

release 方法下,会履行两个指令:

它会先生成 app.dill,并且这儿分为两种方法: app-aot-assemble 和 app-aot-blobs。iOS 或许 增加了 --build-shared-library会运用 app-aot-assemble,这种方法会生成 App.Framework 或 app.so。Android 默许运用 app-aot-blobs,这种方法会生成 isolate_snapshot_data、isolate_snapshot_instr、vm_snapshot_data 和 vm_snapshot_instr 四个文件。

这儿只会生成 assets。

调试源码

调试是阅览源码最好的辅佐,调试 flutter_tools 的方法比较简单,我用的是 IntelliJ,Android Studio 应该也相似,先导入源码,源码坐落 sdk/packages/flutter_tools;新建一个 Dart Command Line App,设置 Dart file 为 bin/flutter_tools.dart,Program arguments 设置你要调试的指令,比方 build aot,Working directory 则用 flutter create创立个项目即可;终究打好断点,debug 运转。

由于源码中有许多异步代码,用 await 润饰的,能够运用 Force run to Cursor 直接履行到下一行即可。

由于源码中有许多异步代码,用 await 润饰的,能够运用 Force run to Cursor 直接履行到下一行即可。

结束

为了防止篇幅太长,文章尽量防止贴许多的代码,仅仅贴了要害函数,感兴趣的读者能够去阅览相关的源码。

Android 在 release 方法下,运用 app-aot-blobs 方法,用的是 snapshot 文件,这儿要完成动态下发代码,应该仍是有或许的,需求进一步研讨。下一步应该会研讨下热加载的完成,这关于完成动态化应该很有协助。

终究,Flutter 是一个十分好玩的事物,Dart 也是一个十分优异的言语,enjoy it。

"开发者说DTalk" 面向我国开发者们搜集 Google 移动运用 (apps & games) 相关的产品/技能内容。欢迎咱们前来共享您对移动运用的职业洞悉或见地、移动开发进程中的心得或新发现、以及运用出海的实战经验总结和相关产品的运用反应等。咱们由衷地期望能够给这些拔尖的我国开发者们供给更好展现自己、充分发挥自己专长的渠道。咱们将经过咱们的技能内容侧重选出优异事例进行谷歌开发技能专家 (GDE) 的引荐。

了解更多 "开发者说DTalk" 活动概况与参加方法

长按右侧二维码道光通宝-浅谈 Flutter 构建 | 开发者说·DTalk

报名参加