Commit 26432eba authored by John McCutchan's avatar John McCutchan Committed by GitHub

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.
parent 292ba09b
This directory contains a small Flutter application that is run while
an application is being copied onto the device.
......@@ -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<String, int> benchmarkData = new Map<String, int>();
// The initial launch is from a snapshot.
bool _runningFromSnapshot = true;
@override
Future<int> 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<LaunchResult> 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<Uri> _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<OperationResult> _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<String, dynamic> 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);
}
......
......@@ -759,11 +759,21 @@ class Isolate extends ServiceObjectOwner {
static final int kIsolateReloadBarred = 1005;
Future<Map<String, dynamic>> reloadSources({ bool pause: false }) async {
Future<Map<String, dynamic>> reloadSources(
{ bool pause: false,
String rootLibPath,
String packagesPath}) async {
try {
Map<String, dynamic> response = await invokeRpcRaw(
'_reloadSources', <String, dynamic>{ 'pause': pause }
);
Map<String, dynamic> arguments = <String, dynamic>{
'pause': pause
};
if (rootLibPath != null) {
arguments['rootLibUri'] = rootLibPath;
}
if (packagesPath != null) {
arguments['packagesUri'] = packagesPath;
}
Map<String, dynamic> response = await invokeRpcRaw('_reloadSources', arguments);
return response;
} on rpc.RpcException catch(e) {
return new Future<Map<String, dynamic>>.error(<String, dynamic>{
......@@ -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', <String, dynamic> {
'value': message
}).catchError((dynamic error) => null);
}
void flutterLoaderShowExplanation(String explanation) {
// Invoke loaderShowExplanation; ignore any returned errors.
invokeRpcRaw('ext.flutter.loaderShowExplanation', <String, dynamic> {
'value': explanation
}).catchError((dynamic error) => null);
}
void flutterLoaderSetProgress(double progress) {
// Invoke loaderSetProgress; ignore any returned errors.
invokeRpcRaw('ext.flutter.loaderSetProgress', <String, dynamic>{
'loaderSetProgress': progress
}).catchError((dynamic error) => null);
}
void flutterLoaderSetProgressMax(double max) {
// Invoke loaderSetProgressMax; ignore any returned errors.
invokeRpcRaw('ext.flutter.loaderSetProgressMax', <String, dynamic>{
'loaderSetProgressMax': max
}).catchError((dynamic error) => null);
}
static bool _isMethodNotFoundException(dynamic e) {
return (e is rpc.RpcException) &&
(e.code == rpc_error_code.METHOD_NOT_FOUND);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment