Flutter 的热重载(hot reload)
功用能够帮助您在无需从头启动应用的情况下快速、轻松地进行测验、构建用户界面、添加功用以及修正过错。经过将更新后的源代码文件注入正在运转的Dart虚拟机(VM)
中来完成热重载。在虚拟机运用新的的字段和函数更新类后,Flutter 结构会自动从头构建widget树
,以便快速检查更改的作用。
作业原理
JIT 和 AOT 的差异:
JIT(Just In Time):指的是即时编译或运转时编译,在 Debug 形式中运用,能够动态下发和履行代码,启动速度快,但履行功能受运转时编译影响;
AOT(Ahead Of Time):指的是提前编译或运转前编译,在 Release 形式中运用,能够为特定的平台生成安稳的二进制代码,履行功能好、运转速度快,但每次履行均需提前编译,开发调试功率低。
能够看出,这两种编译形式,AOT 是静态编译,最终产品编译成可直接履行的机器码,而 JIT 则是动态编译,dart 代码编译成的是中间代码,在程序运转的时候经过 Dart VM 解说运转。
热重载的步骤:
- 工程改动:热重载
Server
会逐一扫描工程中的文件,检查是否有新增、删除或许改动,直到找到在上次编译之后,发生变化的 Dart 代码。 - 增量编译:热重载模块会将发生变化的 Dart 代码,经过编译转化为增量的
Dart Kernel
文件。 - 推送更新:热重载Server将增量的 Dart Kernel 文件经过
RPC
协议,发送给正在手机上运转的Dart VM
。 - 代码兼并:Dart VM 会将收到的增量 Dart Kernel 文件,与原有的 Dart Kernel 文件进行兼并,然后从头加载新的 Dart Kernel 文件。
- Widget增量烘托:在确认 Dart VM 资源加载成功后,Flutter 会将其 UI 线程重置,通知
flutter.framework
重建 Widget。
不支撑热重载的场景
- main 办法里的更改
- initState 办法里的更改
- 代码呈现编译过错
- 全局变量和静态特点的更改
- Widget 状况无法兼容
- 枚举和泛类型更改
热重载的源码解析
启动流程解析
首要创立一个 flutter 项目 hotload_demo,找到flutter->packages->flutter_tools
,经过 Android Studio 翻开,然后翻开Edit Configurations
,把方才创立的工程 hotload_demo装备Working directory
,Program arguments
先装备一个参数 run
。
- 开端调试运转项目,在
bin->flutter_tools.dart
中的main函数打上断点,能够看到参数有传入进来
- 进入
main
函数,发现在对传进来的参数进行分析,然后进行一些回调处理
- 下面发现调用了
runner.run(args)
办法
- run 办法调用完毕后,会调用
run.dart
里边的runCommand()
办法
- 在 runCommand() 办法里边会找到咱们的设备,这里能够看出设备便是我运转的模拟器设备
iPhone 12 pro Max
- 继续调试发现在终端打印了
Running Xcode build..., xcode build done
, 这些是在mac.dart
里边处理的
调起一些指令给Xcode编译履行
- 在 runcommand() 办法里边,咱们就能够看到
开端注册和启动终端
了
-
setupTerminal()
办法里边就会打印那些帮助信息,什么r 、R
等等,而且下面看到也开端监听终端的输入了
- 能够看下 printHelp 办法的完成,便是输出打印了那些帮助信息
- 发现终端最下面输出了两个 URL ,上面这个是
dart虚拟机
的,下面这个是Devtools
调试工具,Devtools 能够进行功能的调试
扫描增量文件
- 监听终端的输入,然后进行处理
这里能够看到输入 r
对应的处理,热更新首要履行的代码
- 输入
r
进行hot reload
刷新展示residentRunner.restart(fullRestart: false)
,restart 中继续履行下面的办法
- 跟进 restart 办法里边的
_fullRestartHelper
办法和_reloadSources
办法对资源文件进行从头加载
- 记录 hotreload 的加载时间,然后更新增量文件
- 跟进
_updateDevFS
更新增量文件,找到设备履行更新文件,再跟进去找到了update办法
- 获取
content
中的途径然后查找这个文件,然后终端进入这个目录,输入strings app.dill.incremental.dill
,假如有更改内容就能够输出更改文件的内容
- 这便是依据途径找到的文件,
app.dill.incremental.dill
就包含增量的文件内容
传输添加文件给 Dart VM
- 传输增量文件
DevFSContent
给虚拟机,经过_httpWriter.writer
异步走RPC
协议写入到 DartVM中更新await _httpWriter.write(dirtyEntries);
,
能够检查这个_httpWriter
里边有个特点 httpAddress
便是 Dart VM 的地址
- 那虚拟机什么时候创立呢,其实打断点能够看到便是开端
run
的时候就创立了,创立里边就会有一系列的注册
热重载引擎的联调
因为之前我是下载了flutter 引擎代码的,然后编译成了对应的iOS工程,所以这里能够进行联调。
- 怎么让 Flutter 工程中的iOS工程运用本地编译好的引擎,需求在
Runner
的Generated.xcconfig
里边装备引擎
//编译引擎的途径
FLUTTER_ENGINE=/Users/zhou/engine_download/src
//对应的模拟器debug版本
LOCAL_ENGINE=ios_debug_sim_unopt
装备完成后就开端运转Runner工程,我在flutter 引擎里边在点击办法里边添加了一个打印
当我运转其他项目后,点击屏幕控制台输出了 点击了,这就说明晰挂载成功了
- 接下来联调
flutter tools
,需求在Edit Configurations
里边Program arguments
装备参数
run --local-engine-src-path /Users/zhou/engine_download/src --local-engine=ios_debug_sim_unopt
跑起来项目后,然后在 Xcode->Debug->Attach to process
里边附加工程,再打进入 /Users/zhou/engine_download/src/out/ios_debug_sim_unopt 翻开iOS工程,找到这个办法,这个便是咱们更新加载增量文件要调用的办法
在方才运转的Xcode工程里边添加断点
br set -n "IsolateGroupReloadContext::Reload"
这样就形成了一个闭环,引擎代码、Flutter 代码,flutter tools
,然后需改Flutter代码,在 flutter tools 里边履行 r
,发现Xcode断点到了 IsolateGroupReloadContext
这个办法开端履行添加烘托,说明验证了之前的热重载的步骤。
总结
这样一步步的调试下来就能大概明白热重载的原理。Flutter 的热重载是基于 JIT 编译形式的代码增量同步。因为 JIT 属于动态编译,能够将 Dart 代码编译成生成中间代码,让 Dart VM 在运转时解说履行,因此能够经过动态更新中间代码完成增量同步。
另一方面,因为涉及到状况的保存与康复,涉及状况兼容与状况初始化的场景,热重载是无法支撑的,如改动前后 Widget 状况无法兼容、全局变量与静态特点的更改、main 办法里的更改、initState 办法里的更改、枚举和泛型的更改等。
能够发现,热重载提高了调试 UI 的功率,十分适合写界面款式这样需求反复检查修正作用的场景。但因为其状况保存的机制所限,热重载本身也有一些无法支撑的鸿沟。
参阅文章:
www.jianshu.com/p/46b43554b…
zhuanlan.zhihu.com/p/89870807