对于开发者而言,什么是Flutter?它是用什么语言编写的,包含哪几部分,是如何被编译,运行到设备上的呢?Flutter如何做到Debug模式Hot Reload快速生效变更,Release模式原生体验的呢?Flutter工程和我们的Android/iOS工程有何差别,关系如何,又是如何嵌入Android/iOS的呢?Flutter的渲染和事件传递机制如何工作?Flutter支持热更新吗?Flutter官方并未提供iOS下的armv7支持,确实如此吗?在使用Flutter的时候,如果发现了engine的bug,如何去修改和生效?构建缓慢或出错又如何去定位,修改和生效呢?
凡此种种,都需要对Flutter从设计,开发构建,到最终运行有一个全局视角的观察。
本文将以一个简单的hello_flutter为例,介绍下Flutter相关原理及定制与优化。
Flutter简介
- Flutter的架构主要分成三层:Framework,Engine和Embedder。
- Framework使用dart实现,包括Material Design风格的Widget,Cupertino(针对iOS)风格的
- Widgets,文本/图片/按钮等基础Widgets,渲染,动画,手势等。此部分的核心代码
- 是:flutter仓库下的flutter package,以及sky_engine仓库下的io,async,ui(dart:ui库提供了
- Flutter框架和引擎之间的接口)等package。
- Engine使用C++实现,主要包括:Skia,Dart和Text。Skia是开源的二维图形库,提供了适用于
- 多种软硬件平台的通用API。其已作为Google Chrome,Chrome OS,Android, Mozilla
- Firefox, Firefox OS等其他众多产品的图形引擎,支持平台还包括Windows7+,macOS
- 10.10.5+,iOS8+,Android4.1+,Ubuntu14.04+等。Dart部分主要包括:Dart Runtime,
- Garbage Collection(GC),如果是Debug模式的话,还包括JIT(Just In Time)支持。Release
- 和Profile模式下,是AOT(Ahead Of Time)编译成了原生的arm代码,并不存在JIT部分。Text
- 即文本渲染,其渲染层次如下:衍生自minikin的libtxt库(用于字体选择,分隔行)。HartBuzz用
- 于字形选择和成型。Skia作为渲染/GPU后端,在Android和Fuchsia上使用FreeType渲染,
- 在iOS上使用CoreGraphics来渲染字体。
- Embedder是一个嵌入层,即把Flutter嵌入到各个平台上去,这里做的主要工作包括渲染
- Surface设置,线程设置,以及插件等。从这里可以看出,Flutter的平台相关层很低,平台(如
- iOS)只是提供一个画布,剩余的所有渲染相关的逻辑都在Flutter内部,这就使得它具有了很好
- 的跨端一致性。
Flutter工程结构
本文使用开发环境为flutter beta v0.3.1,对应的engine commit:09d05a389。
以hello_flutter工程为例,Flutter工程结构如下所示:
- 其中ios为iOS部分代码,使用CocoaPods管理依赖,android为Android部分代码,使用
- Gradle管理依赖,lib为dart代码,使用pub管理依赖。类似iOS中Cocoapods对应的Podfile
- 和Podfile.lock,pub下则是pubspec.yaml和pubspec.lock。
Flutter模式
- 对于Flutter,它支持常见的debug,release,profile等模式,但它又有其不一样。
- Debug模式:对应了Dart的JIT模式,又称检查模式或者慢速模式。支持设备,模拟器
- (iOS/Android),此模式下打开了断言,包括所有的调试信息,服务扩展和Observatory等调
- 试辅助。此模式为快速开发和运行做了优化,但并未对执行速度,包大小和部署做优化。
- Debug模式下,编译使用JIT技术,支持广受欢迎的亚秒级有状态的hot reload。
- Release模式:对应了Dart的AOT模式,此模式目标即为部署到终端用户。只支持真机,不包
- 括模拟器。关闭了所有断言,尽可能多地去掉了调试信息,关闭了所有调试工具。为快速启
- 动,快速执行,包大小做了优化。禁止了所有调试辅助手段,服务扩展。
- Profile模式:类似Release模式,只是多了对于Profile模式的服务扩展的支持,支持跟踪,以
- 及最小化使用跟踪信息需要的依赖,例如,observatory可以连接上进程。Profile并不支持模
- 拟器的原因在于,模拟器上的诊断并不代表真实的性能。
- 鉴于Profile同Release在编译原理等上无差异,本文只讨论Debug和Release模式。
- 事实上flutter下的iOS/Android工程本质上依然是一个标准的iOS/Android的工程,flutter只
- 是通过在BuildPhase中添加shell来生成和嵌入App.framework和Flutter.framework(iOS),通
- 过gradle来添加flutter.jar和vm/isolate_snapshot_data/instr(Android)来将Flutter相关代码
- 编译和嵌入原生App而已。因此本文主要讨论因flutter引入的构建,运行等原理。编译target
- 虽然包括arm,x64,x86,arm64,但因原理类似,本文只讨论arm相关(如无特殊说明,android
- 默认为armv7)。
Flutter代码的编译与运行(iOS)
Release模式下的编译
release模式下,flutter下iOS工程中dart代码构建链路如下所示:
- 其中gen_snapshot是dart编译器,采用了tree shaking(类似依赖树逻辑,可生成最小包也
- 因而在Flutter中禁止了dart支持的反射特性)等技术,用于生成汇编形式的机器代码,再通过
- xcrun等编译工具链生成最终的App.framework。换句话说,所有的dart代码,包括业务代
- 码,三方package代码,它们所依赖的flutter框架代码,最终将会变成App.framework。
- tree shaking功能位于gen_snapshot中,对应逻辑参见:
- engine/src/third_party/dart/runtime/vm/compiler/aot/http://precompiler.cc
- dart代码最终对应到App.framework中的符号如下所示:
- 事实上,类似Android Release下的产物(见下文),App.framework也包含了
- kDartVmSnapshotData,kDartVmSnapshotInstructionskDartIsolateSnapshotData,
- kDartIsolateSnapshotInstructions四个部分。为什么iOS使用App.framework这种方式而
- 不是Android的四个文件的方式呢?原因在于在iOS下,因为系统的限制,Flutter引擎不够
- 在运行时将某内存页标记为可执行,而Android是可以的。
- Flutter.framework对应了Flutter架构中的engine部分,以及Embedder。实际中
- Flutter.framework位于flutter仓库的/bin/cache/artifacts/engine/ios*下,默认从google仓
- 库拉取。当需要自定义修改的时候,可通过下载engine源码,利用Ninja构建系统来生成。
- Flutter相关代码的最终产物是:App.framework(dart代码生成)和Flutter.framework(引擎)。
- 从Xcode工程的视角看,Generated.xcconfig描述了Flutter相关环境的配置信息,然后
- Runner工程设置中的Build Phases新增的xcode_backend.sh实现了Flutter.framework的拷
- 贝(从Flutter仓库的引擎到Runner工程根目录下的Flutter目录)与嵌入和App.framework的编
- 译与嵌入。最终生成的Runner.app中Flutter相关内容如下所示:
- 其中flutter_assets是相关的资源,代码则是位于Frameworks下的App.framework和
- Flutter.framework。
- Debug模式下的编译
Debug模式下flutter的编译,结构类似Release模式,差异主要表现为两点:
1.Flutter.framework
因为是Debug,此模式下Framework中是有JIT支持的,而在Release模式下并没有JIT部分。
2.App.framework
- 不同于AOT模式下的App.framework是Dart代码对应的本地机器代码,JIT模式下,
- App.framework只有几个简单的API,其Dart代码存在于snapshot_blob.bin文件里。这部分
- 的snapshot是脚本快照,里面是简单的标记化的源代码。所有的注释,空白字符都被移除,
- 常量也被规范化,也没有机器码,tree shaking或者是混淆。
- App.framework中的符号表如下所示:
对Runner.app/flutter_assets/snapshot_blob.bin执行strings命令可以看到如下内容:
- Debug模式下main入口的调用堆栈如下:
- debug isolate main callstack
- Flutter代码的编译与运行(Android)
- 鉴于Android和iOS除了部分平台相关的特性外,其他逻辑如Release对应AOT,Debug对应
- JIT等均类似,此处只涉及两者不同。
- Release模式下的编译
release模式下,flutter下Android工程中dart代码整个构建链路如下所示:
- 其中vm/isolate_snapshot_data/instr内容均为arm指令,将会在运行时被engine载入,并标
- 记vm/isolate_snapshot_instr为可执行。vm_中涉及runtime等服务(如gc),用于初始化
- DartVM,调用入口见Dart_Initialize(dart_api.h)。isolate__则是对应了我们的App代码,用
- 于创建一个新的isolate,调用入口见Dart_CreateIsolate(dart_api.h)。flutter.jar类似iOS的
- Flutter.framework,包括了engine部分的代码(Flutter.jar中的libflutter.so),以及一套将
- Flutter嵌入Android的类和接口(FlutterMain,FlutterView,FlutterNativeView等)。实际中
- flutter.jar位于flutter仓库的/bin/cache/artifacts/engine/android*下,默认从google仓库
- 拉取。当需要自定义修改的时候,可通过下载engine源码,利用Ninja构建系统来生成flutter.jar。
正在跳转(iOS交流裙 密码:123)