热重载启动流程
我们在Dart代码上修改了一下内容,马上就能在客户端上看到效果。这种增量更新的机制是怎么样的?今天可以来试着研究一下。前面介绍我们知道flutter的源码在这里
项目配置
打开flutter_tools指定需要挂载的调试代码
点击”+“
配置文件
- Dart file: 源码flutter_tools中
flutter_tools.dart的路径 - Program arguments: run(运行)
 - Working directory:搭载的测试项目(需要调试的flutter项目)
 

如果此时下面提示Dart SDK is not Configured in Flutter解决方法也比较简单,打开AS的Preferences然后Apply之后即可。
设置了之后就可以正常运行成功。
断点调试
在flutter_tools.dart文件中的main函数出下个断点,来简单的跟一下设备启动之前的流程,main函数中的参数是run也就是上面配置文件中配置的run
然后到了runner.run ->generateCommands -> runCommand�() -> ..registerSignalHandlers()注册 -> ..setupTerminal()启动终端 - >residentRunner.printHelp
这里的..setupTerminal()运行之后,控制台终端命令就出来了, 这些输入都是在printHelp函数中可以找到
@overridevoid printHelp({ @required bool details }) {globals.printStatus('Flutter run key commands.');commandHelp.r.print();if (supportsRestart) {commandHelp.R.print();}if (details) {printHelpDetails();commandHelp.hWithDetails.print();} else {commandHelp.hWithoutDetails.print();}if (_didAttach) {commandHelp.d.print();}commandHelp.c.print();commandHelp.q.print();globals.printStatus('');if (debuggingOptions.buildInfo.nullSafetyMode == NullSafetyMode.sound) {globals.printStatus('💪 Running with sound null safety 💪', emphasis: true);} else {globals.printStatus('Running with unsound null safety',emphasis: true,);globals.printStatus('For more information see https://dart.dev/null-safety/unsound-null-safety',);}globals.printStatus('');printDebuggerList();}

这里的第一个地址是虚拟机的地址,第二个地址是debug的工具。
Hot reload
r Hot reload.r 即为热重载,那么我们输入r的时候,具体调用了什么,接着往下看:setupTerminal() -> _terminal.keystrokes.listen(processTerminalInput) -> processTerminalInput -> _commonTerminalInputHandler(command) -> residentRunner.restart(fullRestart: false)来到了run_hot.dart中的这个restart的方法 -> _hotReloadHelper
-> _reloadSources(这里就是加载改动的dart代码) -> device.vmService.getFlutterViews(获取vm虚拟机) -> _updateDevFS(增量更新)
for (final FlutterDevice device in flutterDevices) {results.incorporateResults(await device.updateDevFS(mainUri: entrypointFile.absolute.uri,target: target,bundle: assetBundle,firstBuildTime: firstBuildTime,bundleFirstUpload: isFirstUpload,bundleDirty: !isFirstUpload && rebuildBundle,fullRestart: fullRestart,projectRootPath: projectRootPath,pathToReload: getReloadPath(fullRestart: fullRestart, swap: _swap),invalidatedFiles: invalidationResult.uris,packageConfig: invalidationResult.packageConfig,dillOutputPath: dillOutputPath,));}
然后在for循环内-> device.updateDevFS -> devFS.update
断点调试这里可以看到这里的编译二进制文件的一个地址
前往文件夹,打开该地址,查看文件
可以看出这个文件没有内容,我们在刚刚被挂载的项目my_flutter的测试demo中,修改一下main.dart的内容或者增加个注释也可
title: 'Demo11111111', // 测试由’Demo‘修改为’Demo11111111‘
然后回到flutter_tools.dart中,在控制台中输入r又来到了刚才的断点处
找到改地址,打开看下文件的变化
这里就可以明显的看到app.dill.incremental.dill这个文件就是增量的二进制文件的内容。由此,可以得出在dart中增量渲染是以文件为单位的,即使只增加了注释也是一样的效果。
虚拟机
拿到这个值之后怎么传给虚拟机呢?dirtyEntries的value的值就是刚才增量文件的地址
await (devFSWriter ?? _httpWriter).write(dirtyEntries, _baseUri, _httpWriter);

_httpWriter的httpAddress即是上文中提到的虚拟机的地址
所以这行代码就是从dar本地给发送到了虚拟机。那么什么时候创建的虚拟机呢,来到vmservice.dart文件中的setUpVmService方法
try {await Future.wait(registrationRequests);} on vm_service.RPCError catch (e) {throwToolExit('Failed to register service methods on attached VM Service: $e');}
这里虚拟机使用RPC与flutter.framework建立联系,也就是和flutter引擎交互。
指定flutter引擎
在测试的代码里面指定调试的引擎,这样xx.dart ->flutter.tools -> 虚拟机 -> framework引擎就形成了一个闭关。可以随时检测调试,指定引擎的方式,找到demo的iOS工程端配置下
// ios配置文件指定引擎FLUTTER_ENGINE=/Users/liukun/engine/srcLOCAL_ENGINE=ios_debug_sim_unopt
回到flutter.tools的配置项,指定Program arguments更改为run --local-engine-src-path /Users/liukun/engine/src --local-engine=ios_debug_sim_unopt 
- 运行
flutter.tools - 打开Xcode iOS Runner,
Debug->Attach to Process 

�点击Pause program execution能断点成功就表明已经连接上了,接着来验证一下完整的流程
完整链路验证
- 1.修改demo中的dart文件
 - 回到flutter_tools中断点在如下位置
 

3.回到Xcode中,下一个断点,该IsolateGroupReloadContext::Reload方法是引擎收到dart虚拟机发送的context需要更新的地方。
4.在控制台输入r之后过了上面的2个断点之后就直接来到了3的断点
归纳总结之后,整个完整的链路大概是下面这种情况:

