Add time to frame tracking to hot run (#5316)
diff --git a/packages/flutter_tools/lib/src/base/utils.dart b/packages/flutter_tools/lib/src/base/utils.dart
index 0248ff3..7207d88 100644
--- a/packages/flutter_tools/lib/src/base/utils.dart
+++ b/packages/flutter_tools/lib/src/base/utils.dart
@@ -78,6 +78,15 @@
return '${(bytesLength / (1024 * 1024)).toStringAsFixed(1)}MB';
}
+String getElapsedAsSeconds(Duration duration) {
+ double seconds = duration.inMilliseconds / Duration.MILLISECONDS_PER_SECOND;
+ return '${seconds.toStringAsFixed(2)} seconds';
+}
+
+String getElapsedAsMilliseconds(Duration duration) {
+ return '${duration.inMilliseconds} ms';
+}
+
/// Return a relative path if [fullPath] is contained by the cwd, else return an
/// absolute path.
String getDisplayPath(String fullPath) {
diff --git a/packages/flutter_tools/lib/src/hot.dart b/packages/flutter_tools/lib/src/hot.dart
index 8dbdb59..f69de8f 100644
--- a/packages/flutter_tools/lib/src/hot.dart
+++ b/packages/flutter_tools/lib/src/hot.dart
@@ -29,6 +29,41 @@
'loader_app.dart'));
}
+class FirstFrameTimer {
+ FirstFrameTimer(this.serviceProtocol);
+
+ void start() {
+ stopwatch.reset();
+ stopwatch.start();
+ _subscription = serviceProtocol.onExtensionEvent.listen(_onExtensionEvent);
+ }
+
+ /// Returns a Future which completes after the first frame event is received.
+ Future<Null> firstFrame() => _completer.future;
+
+ void _onExtensionEvent(Event event) {
+ if (event.extensionKind == 'Flutter.FirstFrame')
+ _stop();
+ }
+
+ void _stop() {
+ _subscription?.cancel();
+ _subscription = null;
+ stopwatch.stop();
+ _completer.complete(null);
+ }
+
+ Duration get elapsed {
+ assert(!stopwatch.isRunning);
+ return stopwatch.elapsed;
+ }
+
+ final Observatory serviceProtocol;
+ final Stopwatch stopwatch = new Stopwatch();
+ final Completer<Null> _completer = new Completer<Null>();
+ StreamSubscription<Event> _subscription;
+}
+
class HotRunner extends ResidentRunner {
HotRunner(
Device device, {
@@ -347,16 +382,21 @@
}
Future<Null> _restartFromSources() async {
+ FirstFrameTimer firstFrameTimer = new FirstFrameTimer(serviceProtocol);
+ firstFrameTimer.start();
if (_devFS == null) {
- Status restartStatus = logger.startProgress('Restarting application...');
await _launchFromDisk(_package, _mainPath);
- restartStatus.stop(showElapsedTime: true);
} else {
await _updateDevFS();
- Status restartStatus = logger.startProgress('Restarting application...');
await _launchFromDevFS(_package, _mainPath);
- restartStatus.stop(showElapsedTime: true);
}
+ Status restartStatus =
+ logger.startProgress('Waiting for application to start...');
+ // Wait for the first frame to be rendered.
+ await firstFrameTimer.firstFrame();
+ restartStatus.stop(showElapsedTime: true);
+ printStatus('Restart time: '
+ '${getElapsedAsMilliseconds(firstFrameTimer.elapsed)}');
flutterUsage.sendEvent('hot', 'restart');
}
@@ -380,6 +420,8 @@
Future<bool> _reloadSources() async {
if (serviceProtocol.firstIsolateId == null)
throw 'Application isolate not found';
+ FirstFrameTimer firstFrameTimer = new FirstFrameTimer(serviceProtocol);
+ firstFrameTimer.start();
if (_devFS != null)
await _updateDevFS();
Status reloadStatus = logger.startProgress('Performing hot reload...');
@@ -410,6 +452,9 @@
return false;
}
reassembleStatus.stop(showElapsedTime: true);
+ await firstFrameTimer.firstFrame();
+ printStatus('Hot reload time: '
+ '${getElapsedAsMilliseconds(firstFrameTimer.elapsed)}');
return true;
}