From 26432eba305375e8034d183512ffa6a3fbcefa8d Mon Sep 17 00:00:00 2001 From: John McCutchan Date: Tue, 22 Nov 2016 14:16:20 -0800 Subject: [PATCH] Boot the application directly even when hot reloading. (#6948) - [x] Remove the two stage initial boot process. - [x] Remove the loader screen app. - [x] Don't report reload times for the initial reload because we are switching from a snapshot to source and that will always be worst case. --- packages/flutter/bin/loader/README.md | 3 - packages/flutter_tools/lib/src/hot.dart | 80 +++++++------------ packages/flutter_tools/lib/src/vmservice.dart | 48 ++++------- 3 files changed, 44 insertions(+), 87 deletions(-) delete mode 100644 packages/flutter/bin/loader/README.md diff --git a/packages/flutter/bin/loader/README.md b/packages/flutter/bin/loader/README.md deleted file mode 100644 index bf8e3004bc5..00000000000 --- a/packages/flutter/bin/loader/README.md +++ /dev/null @@ -1,3 +0,0 @@ -This directory contains a small Flutter application that is run while -an application is being copied onto the device. - diff --git a/packages/flutter_tools/lib/src/hot.dart b/packages/flutter_tools/lib/src/hot.dart index 53ed1c4f0c9..9ebe91e97b5 100644 --- a/packages/flutter_tools/lib/src/hot.dart +++ b/packages/flutter_tools/lib/src/hot.dart @@ -17,7 +17,6 @@ import 'base/logger.dart'; import 'base/process.dart'; import 'base/utils.dart'; import 'build_info.dart'; -import 'cache.dart'; import 'dart/package_map.dart'; import 'devfs.dart'; import 'device.dart'; @@ -37,15 +36,6 @@ HotRunnerConfig get hotRunnerConfig => context[HotRunnerConfig]; const bool kHotReloadDefault = true; -String getDevFSLoaderScript() { - return path.absolute(path.join(Cache.flutterRoot, - 'packages', - 'flutter', - 'bin', - 'loader', - 'loader_app.dart')); -} - class DartDependencySetBuilder { DartDependencySetBuilder(this.mainScriptPath, this.projectRootPath, @@ -123,6 +113,8 @@ class HotRunner extends ResidentRunner { AssetBundle get bundle => _bundle; final bool benchmarkMode; final Map benchmarkData = new Map(); + // The initial launch is from a snapshot. + bool _runningFromSnapshot = true; @override Future run({ @@ -203,13 +195,13 @@ class HotRunner extends ResidentRunner { await startEchoingDeviceLog(_package); - printTrace('Launching loader on ${device.name}...'); + printStatus('Launching ${getDisplayPath(_mainPath)} on ${device.name}...'); - // Start the loader. + // Start the application. Future futureResult = device.startApp( _package, debuggingOptions.buildMode, - mainPath: getDevFSLoaderScript(), + mainPath: _mainPath, debuggingOptions: debuggingOptions, platformArgs: platformArgs, route: route, @@ -219,7 +211,7 @@ class HotRunner extends ResidentRunner { LaunchResult result = await futureResult; if (!result.started) { - printError('Error launching DevFS loader on ${device.name}.'); + printError('Error launching application on ${device.name}.'); await stopEchoingDeviceLog(); return 2; } @@ -248,16 +240,8 @@ class HotRunner extends ResidentRunner { printError('Error initializing DevFS: $error'); return 3; } - _loaderShowMessage('Connecting...', progress: 0); - _loaderShowExplanation('You can use hot reload to update your app on the fly, without restarting it.'); - bool devfsResult = await _updateDevFS( - progressReporter: (int progress, int max) { - if (progress % 10 == 0) - _loaderShowMessage('Syncing files to device...', progress: progress, max: max); - } - ); + bool devfsResult = await _updateDevFS(); if (!devfsResult) { - _loaderShowMessage('Failed.'); printError('Could not perform initial file synchronization.'); return 3; } @@ -265,20 +249,10 @@ class HotRunner extends ResidentRunner { await vmService.vm.refreshViews(); printTrace('Connected to ${vmService.vm.mainView}.'); - printStatus('Running ${getDisplayPath(_mainPath)} on ${device.name}...'); - _loaderShowMessage('Launching...'); - await _launchFromDevFS(_package, _mainPath); - - printTrace('Application running.'); - setupTerminal(); registerSignalHandlers(); - printTrace('Finishing file synchronization'); - // Finish the file sync now. - await _updateDevFS(); - appStartedCompleter?.complete(); if (benchmarkMode) { @@ -316,21 +290,6 @@ class HotRunner extends ResidentRunner { } } - void _loaderShowMessage(String message, { int progress, int max }) { - currentView.uiIsolate.flutterLoaderShowMessage(message); - if (progress != null) { - currentView.uiIsolate.flutterLoaderSetProgress(progress.toDouble()); - currentView.uiIsolate.flutterLoaderSetProgressMax(max?.toDouble() ?? 0.0); - } else { - currentView.uiIsolate.flutterLoaderSetProgress(0.0); - currentView.uiIsolate.flutterLoaderSetProgressMax(-1.0); - } - } - - void _loaderShowExplanation(String explanation) { - currentView.uiIsolate.flutterLoaderShowExplanation(explanation); - } - DevFS _devFS; Future _initDevFS() { @@ -421,6 +380,8 @@ class HotRunner extends ResidentRunner { restartTimer.stop(); printTrace('Restart performed in ' '${getElapsedAsMilliseconds(restartTimer.elapsed)}.'); + // We are now running from sources. + _runningFromSnapshot = false; if (benchmarkMode) { benchmarkData['hotRestartMillisecondsToFrame'] = restartTimer.elapsed.inMilliseconds; @@ -475,6 +436,13 @@ class HotRunner extends ResidentRunner { Future _reloadSources({ bool pause: false }) async { if (currentView.uiIsolate == null) throw 'Application isolate not found'; + // The initial launch is from a script snapshot. When we reload from source + // on top of a script snapshot, the first reload will be a worst case reload + // because all of the sources will end up being dirty (library paths will + // change from host path to a device path). Subsequent reloads will + // not be affected, so we resume reporting reload times on the second + // reload. + final bool shouldReportReloadTime = !_runningFromSnapshot; Stopwatch reloadTimer = new Stopwatch(); reloadTimer.start(); bool updatedDevFS = await _updateDevFS(); @@ -482,8 +450,16 @@ class HotRunner extends ResidentRunner { return new OperationResult(1, 'Dart Source Error'); String reloadMessage; try { + String entryPath = path.relative(_mainPath, from: _projectRootPath); + String deviceEntryPath = + _devFS.baseUri.resolve(entryPath).toFilePath(); + String devicePackagesPath = + _devFS.baseUri.resolve('.packages').toFilePath(); Map reloadReport = - await currentView.uiIsolate.reloadSources(pause: pause); + await currentView.uiIsolate.reloadSources( + pause: pause, + rootLibPath: deviceEntryPath, + packagesPath: devicePackagesPath); if (!_validateReloadReport(reloadReport)) { // Reload failed. flutterUsage.sendEvent('hot', 'reload-reject'); @@ -510,6 +486,8 @@ class HotRunner extends ResidentRunner { } // Reload the isolate. await currentView.uiIsolate.reload(); + // We are now running from source. + _runningFromSnapshot = false; // Check if the isolate is paused. final ServiceEvent pauseEvent = currentView.uiIsolate.pauseEvent; if ((pauseEvent != null) && (pauseEvent.isPauseEvent)) { @@ -534,11 +512,13 @@ class HotRunner extends ResidentRunner { reloadTimer.stop(); printTrace('Hot reload performed in ' '${getElapsedAsMilliseconds(reloadTimer.elapsed)}.'); + if (benchmarkMode) { benchmarkData['hotReloadMillisecondsToFrame'] = reloadTimer.elapsed.inMilliseconds; } - flutterUsage.sendTiming('hot', 'reload', reloadTimer.elapsed); + if (shouldReportReloadTime) + flutterUsage.sendTiming('hot', 'reload', reloadTimer.elapsed); return new OperationResult(OperationResult.ok.code, reloadMessage); } diff --git a/packages/flutter_tools/lib/src/vmservice.dart b/packages/flutter_tools/lib/src/vmservice.dart index e1f8fa2652f..bbd9ef1e5eb 100644 --- a/packages/flutter_tools/lib/src/vmservice.dart +++ b/packages/flutter_tools/lib/src/vmservice.dart @@ -759,11 +759,21 @@ class Isolate extends ServiceObjectOwner { static final int kIsolateReloadBarred = 1005; - Future> reloadSources({ bool pause: false }) async { + Future> reloadSources( + { bool pause: false, + String rootLibPath, + String packagesPath}) async { try { - Map response = await invokeRpcRaw( - '_reloadSources', { 'pause': pause } - ); + Map arguments = { + 'pause': pause + }; + if (rootLibPath != null) { + arguments['rootLibUri'] = rootLibPath; + } + if (packagesPath != null) { + arguments['packagesUri'] = packagesPath; + } + Map response = await invokeRpcRaw('_reloadSources', arguments); return response; } on rpc.RpcException catch(e) { return new Future>.error({ @@ -800,36 +810,6 @@ class Isolate extends ServiceObjectOwner { return invokeFlutterExtensionRpcRaw('ext.flutter.debugDumpRenderTree'); } - // Loader page extension methods. - - void flutterLoaderShowMessage(String message) { - // Invoke loaderShowMessage; ignore any returned errors. - invokeRpcRaw('ext.flutter.loaderShowMessage', { - 'value': message - }).catchError((dynamic error) => null); - } - - void flutterLoaderShowExplanation(String explanation) { - // Invoke loaderShowExplanation; ignore any returned errors. - invokeRpcRaw('ext.flutter.loaderShowExplanation', { - 'value': explanation - }).catchError((dynamic error) => null); - } - - void flutterLoaderSetProgress(double progress) { - // Invoke loaderSetProgress; ignore any returned errors. - invokeRpcRaw('ext.flutter.loaderSetProgress', { - 'loaderSetProgress': progress - }).catchError((dynamic error) => null); - } - - void flutterLoaderSetProgressMax(double max) { - // Invoke loaderSetProgressMax; ignore any returned errors. - invokeRpcRaw('ext.flutter.loaderSetProgressMax', { - 'loaderSetProgressMax': max - }).catchError((dynamic error) => null); - } - static bool _isMethodNotFoundException(dynamic e) { return (e is rpc.RpcException) && (e.code == rpc_error_code.METHOD_NOT_FOUND);