1.关于Dart中的运行方式
JIT:Just In Time . 动态解释,一边翻译一边执行,也称为即时编译,如JavaScript,Python等,在开发周期中使用,可以动态下发和执行代码,开发测试效率高,但是运行速度和性能则会受到影响,Flutter中的热重载正是基于此特性
AOT: Ahead of Time. 静态编译,是指程序在执行前全部被翻译为机器码,提前编译,如 C ,C++ ,OC等,发布时期使用AOT,就不需要像RN那样在跨平台JavaScript代码和原生Android、iOS代码间建立低效的方法调用映射关系。
程序的运行方式和具体的语言没有强制关系,比如python,既可以是JIT 也可以是AOT,Dart是少数同时支持JIT和AOT的语言之一。
Dart在开发过程中使用JIT,每次更改都不需要在编译成字节码,节省了大量时间,在部署中使用AOT生成高效的ARM代码保证高效的性能,所以说Dart具有运行速度快,执行性能好的特点。
2.热重载 Flutter 应用:
Flutter 的热重载功能可帮助您在无需重新启动应用程序的情况下快速、轻松地测试、构建用户界面、添加功能以及修复错误。通过将更新的源代码文件注入到正在运行的 Dart 虚拟机(VM) 来实现热重载。在虚拟机使用新的字段和函数更新类之后, Flutter 框架会自动重新构建 widget 树,以便您可以快速查看更改的效果。
在支持 Flutter 编辑器 或终端窗口运行应用程序,物理机或虚拟器都可以。 Flutter 应用程序只有在调试模式下才能被热重载。
修改项目中的一个Dart文件。大多数类型的代码更改可以热重载;一些需要重新启动应用程序的更改列表,请参阅 特别情况。
-
如果您在支持 Flutter IDE 工具的 IDE /编辑器中工作,请选择 Save All (
cmd-s
/ctrl-s
),或单击工具栏上的 Hot Reload 按钮。如果您正在使用命令行
flutter run
运行应用程序,请在终端窗口输入r
。
3.Flutter中热重载的执行步骤
1.在下载的flutterSDK中找到flutter_tools所在的目录使用Android Studio打开2.点击configuration配置flutter_tool运行的环境,其中 3配置的是运行时传递过去的参数,flutter run或者flutter doctor类型的命令,4中设置的是你要热更新执行的项目,
由于同时在电脑上链接了多个设备,还要配置运行的设备 修改3中的参数 run -d CB6B23E5-1DCD-46E0-9B08-EAEC7CEAFE72
,后面是模拟器的标识。开始运行
5.而在main函数中,主要是一个异步的操作,执行runner.run方法,其中的参数是对各种命令的解析操作
Future main(List args) async {
……………………
//主要查看run方法
await runner.run(args, () => [
AnalyzeCommand(
verboseHelp: verboseHelp,
fileSystem: globals.fs,
platform: globals.platform,
processManager: globals.processManager,
logger: globals.logger,
terminal: globals.terminal,
artifacts: globals.artifacts,
),
AssembleCommand(),
…………………………
6.继续进行跟踪到runner.run目录中,调用runner.dart
文件中 await runner.run(args);
方法,继续开始执行发现到flutter_command_runner.dart
中
@override
Future run(Iterable args) {
// Have an invocation of 'build' print out it's sub-commands.
// TODO(ianh): Move this to the Build command itself somehow.
if (args.length == 1 && args.first == 'build') {
args = ['build', '-h'];
}
//最终执行到run,
return super.run(args);
}
//父类中执行runCommand,此时的args也是传入的参数的值
Future run(Iterable args) =>
Future.sync(() => runCommand(parse(args)));
7.在run.dart
中执行 runCommand方法,获取当前设备的具体信息
8.接下来就是执行runCommand中下面的方法,
final Completer appStartedTimeRecorder = Completer.sync();
// This callback can't throw.
unawaited(appStartedTimeRecorder.future.then(
(_) {
appStartedTime = globals.systemClock.now();
if (stayResident) {
//更新控制台 添加监听
TerminalHandler(runner)
..setupTerminal()
..registerSignalHandlers();
}
}
));
9.跳过中间接下来的几个步骤,在mac.dart文件调用buildXcodeProject
执行xcrun等一系列的步骤,执行以下命令和并开始计时
在上面步骤8中 设置setUpTerminal,对终端输入的命令字进行解析
void setupTerminal() {
if (!globals.logger.quiet) {
globals.printStatus('');
residentRunner.printHelp(details: false);
}
globals.terminal.singleCharMode = true;
subscription = globals.terminal.keystrokes.listen(processTerminalInput);
}
//查看监听方法 processTerminalInput,其中在_commonTerminalInputHandler方法中对输入的命令做了处理
Future _commonTerminalInputHandler(String character) async {
globals.printStatus(''); // the key the user tapped might be on this line
switch(character) {
case 'a':
if (residentRunner.supportsServiceProtocol) {
await residentRunner.debugToggleProfileWidgetBuilds();
return true;
}
return false;
……………………………………………………
//热更新主要执行的代码
case 'r':
if (!residentRunner.canHotReload) {
return false;
}
final OperationResult result = await residentRunner.restart(fullRestart: false);
if (result.fatal) {
throwToolExit(result.message);
}
if (!result.isOk) {
globals.printStatus('Try again after fixing the above error(s).', emphasis: true);
}
return true;
case 'R':
// If hot restart is not supported for all devices, ignore the command.
if (!residentRunner.canHotRestart || !residentRunner.hotMode) {
return false;
}
final OperationResult result = await residentRunner.restart(fullRestart: true);
if (result.fatal) {
throwToolExit(result.message);
}
if (!result.isOk) {
globals.printStatus('Try again after fixing the above error(s).', emphasis: true);
}
return true;
……………………………………………………
}
return false;
}
10.输入r 进行hot reload刷新展示 residentRunner.restart(fullRestart: false); restart中继续执行下面的方法
@override
Future restart({
bool fullRestart = false,
String reason,
bool benchmarkMode = false,
bool silent = false,
bool pause = false,
}) async {
…………………………………………………………
await runSourceGenerators();
if (fullRestart) {
//重点在这里, _fullRestartHelper,_reloadSources方法对资源文件进行重新加载
final OperationResult result = await _fullRestartHelper(
targetPlatform: targetPlatform,
sdkName: sdkName,
emulator: emulator,
reason: reason,
benchmarkMode: benchmarkMode,
silent: silent,
);
……………………………………………………
}
return result;
}
//更新加载资源
Future _reloadSources({
String targetPlatform,
String sdkName,
bool emulator,
bool pause = false,
String reason,
void Function(String message) onSlow,
}) async {
for (final FlutterDevice device in flutterDevices) {
final List views = await device.vmService.getFlutterViews();
for (final FlutterView view in views) {
if (view.uiIsolate == null) {
return OperationResult(2, 'Application isolate not found', fatal: true);
}
}
}
//记录hotreload的加载时间
final Stopwatch reloadTimer = Stopwatch()..start();
final Stopwatch devFSTimer = Stopwatch()..start();
//更新文件--增量更新
final UpdateFSReport updatedDevFS = await _updateDevFS();
// Record time it took to synchronize to DevFS.
bool shouldReportReloadTime = true;
……………………………………………………………………
}
11.更新文件操作
Future _updateDevFS({ bool fullRestart = false }) async {
……………………………………………………………………………………
final UpdateFSReport results = UpdateFSReport(success: true);
//遍历更新文件
for (final FlutterDevice device in flutterDevices) {
//updateDevFS中更新对应文档
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,
));
}
return results;
}
12.在对updateDevFS进行跟踪,最后更新文件的实现
Future update({
@required Uri mainUri,
@required ResidentCompiler generator,
@required bool trackWidgetCreation,
@required String pathToReload,
@required List invalidatedFiles,
@required PackageConfig packageConfig,
String target,
AssetBundle bundle,
DateTime firstBuildTime,
bool bundleFirstUpload = false,
String dillOutputPath,
bool fullRestart = false,
String projectRootPath,
bool skipAssets = false,
}) async {
………………………………………………………………………………………………………………………………
if (!bundleFirstUpload) {
final String compiledBinary = compilerOutput?.outputFilename;
if (compiledBinary != null && compiledBinary.isNotEmpty) {
final Uri entryUri = _fileSystem.path.toUri(projectRootPath != null
? _fileSystem.path.relative(pathToReload, from: projectRootPath)
: pathToReload,
);
//获取content中的路径在文件中查找到这个文件,
//终端中使用 strings app.dill.incremental.dill 文件中包含的就是增量更新的额内容
final DevFSFileContent content = DevFSFileContent(_fileSystem.file(compiledBinary));
syncedBytes += content.size;
//读取到增量代码 添加到服务端
dirtyEntries[entryUri] = content;
}
}
_logger.printTrace('Updating files');
if (dirtyEntries.isNotEmpty) {
try {
//添加到增量更新里面 通过网络注入到 DartVM中
await _httpWriter.write(dirtyEntries);
} on SocketException catch (socketException, stackTrace) {
_logger.printTrace('DevFS sync failed. Lost connection to device: $socketException');
throw DevFSException('Lost connection to device.', socketException, stackTrace);
} on Exception catch (exception, stackTrace) {
_logger.printError('Could not update files on device: $exception');
throw DevFSException('Sync failed', exception, stackTrace);
}
}
_logger.printTrace('DevFS: Sync finished');
return UpdateFSReport(success: true, syncedBytes: syncedBytes,
invalidatedSourcesCount: invalidatedFiles.length);
}
13.接下来 我们更新一次看下对应的变化
根据上面的路径找到对应的文件查看文件内容,strings /private/var/folders/r2/39t1cgw14x3gr81yktts4b940000gn/T/flutter_tools.qvz3CI/flutter_tool.Ydyzh3/app.dill.incremental.dill
增量更新的位置已经发生了变化,添加到增量更新里面 通过_httpWriter.write注入到 DartVM中更新 await _httpWriter.write(dirtyEntries);