Commit 24ab8372 authored by John McCutchan's avatar John McCutchan Committed by GitHub

Support hot reload for applications that don't use the framework (#5868)

parent 05862000
......@@ -107,6 +107,10 @@ abstract class BindingBase {
name: 'exit',
callback: _exitApplication
);
registerSignalServiceExtension(
name: 'frameworkPresent',
callback: () => null
);
assert(() { _debugServiceExtensionsRegistered = true; return true; });
}
......@@ -124,6 +128,7 @@ abstract class BindingBase {
FlutterError.resetErrorCount();
}
/// Registers a service extension method with the given name (full
/// name "ext.flutter.name"), which takes no arguments and returns
/// no value.
......
......@@ -414,19 +414,25 @@ class HotRunner extends ResidentRunner {
firstFrameTimer.start();
await _updateDevFS();
await _launchFromDevFS(_package, _mainPath);
bool waitForFrame =
await currentView.uiIsolate.flutterFrameworkPresent();
Status restartStatus =
logger.startProgress('Waiting for application to start...');
// Wait for the first frame to be rendered.
await firstFrameTimer.firstFrame();
if (waitForFrame) {
// Wait for the first frame to be rendered.
await firstFrameTimer.firstFrame();
}
restartStatus.stop(showElapsedTime: true);
printStatus('Restart time: '
'${getElapsedAsMilliseconds(firstFrameTimer.elapsed)}');
if (benchmarkMode) {
benchmarkData['hotRestartMillisecondsToFrame'] =
firstFrameTimer.elapsed.inMilliseconds;
if (waitForFrame) {
printStatus('Restart time: '
'${getElapsedAsMilliseconds(firstFrameTimer.elapsed)}');
if (benchmarkMode) {
benchmarkData['hotRestartMillisecondsToFrame'] =
firstFrameTimer.elapsed.inMilliseconds;
}
flutterUsage.sendTiming('hot', 'restart', firstFrameTimer.elapsed);
}
flutterUsage.sendEvent('hot', 'restart');
flutterUsage.sendTiming('hot', 'restart', firstFrameTimer.elapsed);
}
/// Returns [true] if the reload was successful.
......@@ -489,22 +495,33 @@ class HotRunner extends ResidentRunner {
await _evictDirtyAssets();
Status reassembleStatus =
logger.startProgress('Reassembling application...');
bool waitForFrame = true;
try {
await currentView.uiIsolate.flutterReassemble();
waitForFrame = (await currentView.uiIsolate.flutterReassemble() != null);
} catch (_) {
reassembleStatus.stop(showElapsedTime: true);
printError('Reassembling application failed.');
return false;
}
reassembleStatus.stop(showElapsedTime: true);
await firstFrameTimer.firstFrame();
printStatus('Hot reload time: '
'${getElapsedAsMilliseconds(firstFrameTimer.elapsed)}');
if (benchmarkMode) {
benchmarkData['hotReloadMillisecondsToFrame'] =
firstFrameTimer.elapsed.inMilliseconds;
try {
/* ensure that a frame is scheduled */
await currentView.uiIsolate.uiWindowScheduleFrame();
} catch (_) {
/* ignore any errors */
}
if (waitForFrame) {
// When the framework is present, we can wait for the first frame
// event and measure reload itme.
await firstFrameTimer.firstFrame();
printStatus('Hot reload time: '
'${getElapsedAsMilliseconds(firstFrameTimer.elapsed)}');
if (benchmarkMode) {
benchmarkData['hotReloadMillisecondsToFrame'] =
firstFrameTimer.elapsed.inMilliseconds;
}
flutterUsage.sendTiming('hot', 'reload', firstFrameTimer.elapsed);
}
flutterUsage.sendTiming('hot', 'reload', firstFrameTimer.elapsed);
return true;
}
......
......@@ -56,7 +56,7 @@ abstract class ResidentRunner {
Future<Null> _debugDumpRenderTree() async {
if (vmService != null)
await vmService.vm.refreshViews();
await currentView.uiIsolate.flutterDebugDumpRenderTree();
}
......
......@@ -7,6 +7,7 @@ import 'dart:convert' show BASE64;
import 'dart:io';
import 'package:json_rpc_2/json_rpc_2.dart' as rpc;
import 'package:json_rpc_2/error_code.dart' as rpc_error_code;
import 'package:web_socket_channel/io.dart';
import 'globals.dart';
......@@ -766,12 +767,28 @@ class Isolate extends ServiceObjectOwner {
// Flutter extension methods.
// Invoke a flutter extension method, if the flutter extension is not
// available, returns null.
Future<Map<String, dynamic>> invokeFlutterExtensionRpcRaw(
String method, [Map<String, dynamic> params]) async {
try {
return await invokeRpcRaw(method, params);
} catch (e) {
// If an application is not using the framework
if (_isMethodNotFoundException(e))
return null;
rethrow;
}
}
// Debug dump extension methods.
Future<Map<String, dynamic>> flutterDebugDumpApp() {
return invokeRpcRaw('ext.flutter.debugDumpApp');
return invokeFlutterExtensionRpcRaw('ext.flutter.debugDumpApp');
}
Future<Map<String, dynamic>> flutterDebugDumpRenderTree() {
return invokeRpcRaw('ext.flutter.debugDumpRenderTree');
return invokeFlutterExtensionRpcRaw('ext.flutter.debugDumpRenderTree');
}
// Loader page extension methods.
......@@ -797,20 +814,36 @@ class Isolate extends ServiceObjectOwner {
}).catchError((dynamic error) => null);
}
/// Causes the application to pick up any changed code.
Future<Map<String, dynamic>> flutterReassemble() {
return invokeRpcRaw('ext.flutter.reassemble');
static bool _isMethodNotFoundException(dynamic e) {
return (e is rpc.RpcException) &&
(e.code == rpc_error_code.METHOD_NOT_FOUND);
}
Future<Map<String, dynamic>> flutterEvictAsset(String assetPath) {
return invokeRpcRaw('ext.flutter.evict', <String, dynamic>{
'value': assetPath
});
// Reload related extension methods.
Future<Map<String, dynamic>> flutterReassemble() async {
return await invokeFlutterExtensionRpcRaw('ext.flutter.reassemble');
}
Future<bool> flutterFrameworkPresent() async {
return (await invokeFlutterExtensionRpcRaw('ext.flutter.frameworkPresent') != null);
}
Future<Map<String, dynamic>> uiWindowScheduleFrame() async {
return await invokeFlutterExtensionRpcRaw('ext.ui.window.scheduleFrame');
}
Future<Map<String, dynamic>> flutterEvictAsset(String assetPath) async {
return await invokeFlutterExtensionRpcRaw('ext.flutter.evict',
<String, dynamic>{
'value': assetPath
}
);
}
Future<Map<String, dynamic>> flutterExit() {
return invokeRpcRaw('ext.flutter.exit').timeout(
const Duration(seconds: 2), onTimeout: () => null);
// Application control extension methods.
Future<Map<String, dynamic>> flutterExit() async {
return await invokeFlutterExtensionRpcRaw('ext.flutter.exit').timeout(
const Duration(seconds: 2), onTimeout: () => null);
}
}
......
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