mirror of
https://github.com/flutter/flutter
synced 2024-10-14 04:02:56 +00:00
Introduce the concept of asynchronicity to the service extensions. (#7823)
This allows us, for example, to wait for the slow mode banner to have been removed from the screen before triggering a screen shot.
This commit is contained in:
parent
a742a5ddf6
commit
dc634e195e
|
@ -2,6 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:collection';
|
||||
|
||||
// COMMON SIGNATURES
|
||||
|
@ -20,16 +21,46 @@ typedef void ValueChanged<T>(T value);
|
|||
/// For example, service extensions use this callback because they
|
||||
/// call the callback whenever the extension is called with a
|
||||
/// value, regardless of whether the given value is new or not.
|
||||
///
|
||||
/// See also:
|
||||
/// * [ValueGetter], the getter equivalent of this signature.
|
||||
/// * [AsyncValueSetter], an asynchronous version of this signature.
|
||||
typedef void ValueSetter<T>(T value);
|
||||
|
||||
/// Signature for callbacks that are to report a value on demand.
|
||||
///
|
||||
/// See also [ValueSetter].
|
||||
/// See also:
|
||||
/// * [ValueSetter], the setter equivalent of this signature.
|
||||
/// * [AsyncValueGetter], an asynchronous version of this signature.
|
||||
typedef T ValueGetter<T>();
|
||||
|
||||
/// Signature for callbacks that filter an iterable.
|
||||
typedef Iterable<T> IterableFilter<T>(Iterable<T> input);
|
||||
|
||||
/// Signature of callbacks that have no arguments and return no data, but that
|
||||
/// return a [Future] to indicate when their work is complete.
|
||||
///
|
||||
/// See also:
|
||||
/// * [VoidCallback], a synchronous version of this signature.
|
||||
/// * [AsyncValueGetter], a signature for asynchronous getters.
|
||||
/// * [AsyncValueSetter], a signature for asynchronous setters.
|
||||
typedef Future<Null> AsyncCallback();
|
||||
|
||||
/// Signature for callbacks that report that a value has been set and return a
|
||||
/// [Future] that completes when the value has been saved.
|
||||
///
|
||||
/// See also:
|
||||
/// * [ValueSetter], a synchronous version of this signature.
|
||||
/// * [AsyncValueGetter], the getter equivalent of this signature.
|
||||
typedef Future<Null> AsyncValueSetter<T>(T value);
|
||||
|
||||
/// Signature for callbacks that are to asynchronously report a value on demand.
|
||||
///
|
||||
/// See also:
|
||||
/// * [ValueGetter], a synchronous version of this signature.
|
||||
/// * [AsyncValueSetter], the setter equivalent of this signature.
|
||||
typedef Future<T> AsyncValueGetter<T>();
|
||||
|
||||
|
||||
// BITFIELD
|
||||
|
||||
|
|
|
@ -113,7 +113,7 @@ abstract class BindingBase {
|
|||
);
|
||||
registerSignalServiceExtension(
|
||||
name: 'frameworkPresent',
|
||||
callback: () => null
|
||||
callback: () => new Future<Null>.value()
|
||||
);
|
||||
assert(() { _debugServiceExtensionsRegistered = true; return true; });
|
||||
}
|
||||
|
@ -128,8 +128,9 @@ abstract class BindingBase {
|
|||
/// code, and to flush any caches of previously computed values, in
|
||||
/// case the new code would compute them differently.
|
||||
@mustCallSuper
|
||||
void reassembleApplication() {
|
||||
Future<Null> reassembleApplication() {
|
||||
FlutterError.resetErrorCount();
|
||||
return new Future<Null>.value();
|
||||
}
|
||||
|
||||
|
||||
|
@ -141,14 +142,14 @@ abstract class BindingBase {
|
|||
@protected
|
||||
void registerSignalServiceExtension({
|
||||
@required String name,
|
||||
@required VoidCallback callback
|
||||
@required AsyncCallback callback
|
||||
}) {
|
||||
assert(name != null);
|
||||
assert(callback != null);
|
||||
registerServiceExtension(
|
||||
name: name,
|
||||
callback: (Map<String, String> parameters) async {
|
||||
callback();
|
||||
await callback();
|
||||
return <String, dynamic>{};
|
||||
}
|
||||
);
|
||||
|
@ -169,8 +170,8 @@ abstract class BindingBase {
|
|||
@protected
|
||||
void registerBoolServiceExtension({
|
||||
String name,
|
||||
@required ValueGetter<bool> getter,
|
||||
@required ValueSetter<bool> setter
|
||||
@required AsyncValueGetter<bool> getter,
|
||||
@required AsyncValueSetter<bool> setter
|
||||
}) {
|
||||
assert(name != null);
|
||||
assert(getter != null);
|
||||
|
@ -179,8 +180,8 @@ abstract class BindingBase {
|
|||
name: name,
|
||||
callback: (Map<String, String> parameters) async {
|
||||
if (parameters.containsKey('enabled'))
|
||||
setter(parameters['enabled'] == 'true');
|
||||
return <String, dynamic>{ 'enabled': getter() };
|
||||
await setter(parameters['enabled'] == 'true');
|
||||
return <String, dynamic>{ 'enabled': await getter() };
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -199,8 +200,8 @@ abstract class BindingBase {
|
|||
@protected
|
||||
void registerNumericServiceExtension({
|
||||
@required String name,
|
||||
@required ValueGetter<double> getter,
|
||||
@required ValueSetter<double> setter
|
||||
@required AsyncValueGetter<double> getter,
|
||||
@required AsyncValueSetter<double> setter
|
||||
}) {
|
||||
assert(name != null);
|
||||
assert(getter != null);
|
||||
|
@ -209,8 +210,8 @@ abstract class BindingBase {
|
|||
name: name,
|
||||
callback: (Map<String, String> parameters) async {
|
||||
if (parameters.containsKey(name))
|
||||
setter(double.parse(parameters[name]));
|
||||
return <String, dynamic>{ name: getter() };
|
||||
await setter(double.parse(parameters[name]));
|
||||
return <String, dynamic>{ name: await getter() };
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -228,8 +229,8 @@ abstract class BindingBase {
|
|||
@protected
|
||||
void registerStringServiceExtension({
|
||||
@required String name,
|
||||
@required ValueGetter<String> getter,
|
||||
@required ValueSetter<String> setter
|
||||
@required AsyncValueGetter<String> getter,
|
||||
@required AsyncValueSetter<String> setter
|
||||
}) {
|
||||
assert(name != null);
|
||||
assert(getter != null);
|
||||
|
@ -238,8 +239,8 @@ abstract class BindingBase {
|
|||
name: name,
|
||||
callback: (Map<String, String> parameters) async {
|
||||
if (parameters.containsKey('value'))
|
||||
setter(parameters['value']);
|
||||
return <String, dynamic>{ 'value': getter() };
|
||||
await setter(parameters['value']);
|
||||
return <String, dynamic>{ 'value': await getter() };
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -300,6 +301,6 @@ abstract class BindingBase {
|
|||
}
|
||||
|
||||
/// Terminate the Flutter application.
|
||||
void _exitApplication() {
|
||||
Future<Null> _exitApplication() async {
|
||||
exit(0);
|
||||
}
|
||||
|
|
|
@ -54,6 +54,7 @@ const int _kDebugPrintCapacity = 16 * 1024;
|
|||
const Duration _kDebugPrintPauseTime = const Duration(seconds: 1);
|
||||
final Queue<String> _debugPrintBuffer = new Queue<String>();
|
||||
final Stopwatch _debugPrintStopwatch = new Stopwatch();
|
||||
Completer<Null> _debugPrintCompleter;
|
||||
bool _debugPrintScheduled = false;
|
||||
void _debugPrintTask() {
|
||||
_debugPrintScheduled = false;
|
||||
|
@ -71,11 +72,19 @@ void _debugPrintTask() {
|
|||
_debugPrintScheduled = true;
|
||||
_debugPrintedCharacters = 0;
|
||||
new Timer(_kDebugPrintPauseTime, _debugPrintTask);
|
||||
_debugPrintCompleter ??= new Completer<Null>();
|
||||
} else {
|
||||
_debugPrintStopwatch.start();
|
||||
_debugPrintCompleter?.complete();
|
||||
_debugPrintCompleter = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// A Future that resolves when there is no longer any buffered content being
|
||||
/// printed by [debugPrintThrottled] (which is the default implementation for
|
||||
/// [debugPrint], which is used to report errors to the console).
|
||||
Future<Null> get debugPrintDone => _debugPrintCompleter?.future ?? new Future<Null>.value();
|
||||
|
||||
final RegExp _indentPattern = new RegExp('^ *(?:[-+*] |[0-9]+[.):] )?');
|
||||
enum _WordWrapParseMode { inSpace, inWord, atBreak }
|
||||
/// Wraps the given string at the given width.
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:developer';
|
||||
import 'dart:ui' as ui show window;
|
||||
|
||||
|
@ -51,12 +52,13 @@ abstract class RendererBinding extends BindingBase implements SchedulerBinding,
|
|||
// this service extension only works in checked mode
|
||||
registerBoolServiceExtension(
|
||||
name: 'debugPaint',
|
||||
getter: () => debugPaintSizeEnabled,
|
||||
getter: () async => debugPaintSizeEnabled,
|
||||
setter: (bool value) {
|
||||
if (debugPaintSizeEnabled == value)
|
||||
return;
|
||||
return new Future<Null>.value();
|
||||
debugPaintSizeEnabled = value;
|
||||
_forceRepaint();
|
||||
return endOfFrame;
|
||||
}
|
||||
);
|
||||
return true;
|
||||
|
@ -64,19 +66,20 @@ abstract class RendererBinding extends BindingBase implements SchedulerBinding,
|
|||
|
||||
registerSignalServiceExtension(
|
||||
name: 'debugDumpRenderTree',
|
||||
callback: debugDumpRenderTree
|
||||
callback: () { debugDumpRenderTree(); return debugPrintDone; }
|
||||
);
|
||||
|
||||
assert(() {
|
||||
// this service extension only works in checked mode
|
||||
registerBoolServiceExtension(
|
||||
name: 'repaintRainbow',
|
||||
getter: () => debugRepaintRainbowEnabled,
|
||||
getter: () async => debugRepaintRainbowEnabled,
|
||||
setter: (bool value) {
|
||||
bool repaint = debugRepaintRainbowEnabled && !value;
|
||||
debugRepaintRainbowEnabled = value;
|
||||
if (repaint)
|
||||
_forceRepaint();
|
||||
return endOfFrame;
|
||||
}
|
||||
);
|
||||
return true;
|
||||
|
@ -226,8 +229,8 @@ abstract class RendererBinding extends BindingBase implements SchedulerBinding,
|
|||
}
|
||||
|
||||
@override
|
||||
void reassembleApplication() {
|
||||
super.reassembleApplication();
|
||||
Future<Null> reassembleApplication() async {
|
||||
await super.reassembleApplication();
|
||||
Timeline.startSync('Dirty Render Tree');
|
||||
try {
|
||||
renderView.reassemble();
|
||||
|
@ -235,6 +238,7 @@ abstract class RendererBinding extends BindingBase implements SchedulerBinding,
|
|||
Timeline.finishSync();
|
||||
}
|
||||
handleBeginFrame(null);
|
||||
await endOfFrame;
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -157,8 +157,8 @@ abstract class SchedulerBinding extends BindingBase {
|
|||
super.initServiceExtensions();
|
||||
registerNumericServiceExtension(
|
||||
name: 'timeDilation',
|
||||
getter: () => timeDilation,
|
||||
setter: (double value) {
|
||||
getter: () async => timeDilation,
|
||||
setter: (double value) async {
|
||||
timeDilation = value;
|
||||
}
|
||||
);
|
||||
|
@ -441,6 +441,30 @@ abstract class SchedulerBinding extends BindingBase {
|
|||
_postFrameCallbacks.add(callback);
|
||||
}
|
||||
|
||||
Completer<Null> _nextFrameCompleter;
|
||||
|
||||
/// Returns a Future that completes after the frame completes.
|
||||
///
|
||||
/// If this is called between frames, a frame is immediately scheduled if
|
||||
/// necessary. If this is called during a frame, the Future completes after
|
||||
/// the current frame.
|
||||
///
|
||||
/// If the device's screen is currently turned off, this may wait a very long
|
||||
/// time, since frames are not scheduled while the device's screen is turned
|
||||
/// off.
|
||||
Future<Null> get endOfFrame {
|
||||
if (_nextFrameCompleter == null) {
|
||||
if (schedulerPhase == SchedulerPhase.idle)
|
||||
scheduleFrame();
|
||||
_nextFrameCompleter = new Completer<Null>();
|
||||
addPostFrameCallback((Duration timeStamp) {
|
||||
_nextFrameCompleter.complete();
|
||||
_nextFrameCompleter = null;
|
||||
});
|
||||
}
|
||||
return _nextFrameCompleter.future;
|
||||
}
|
||||
|
||||
/// Whether this scheduler has requested that handleBeginFrame be called soon.
|
||||
bool get hasScheduledFrame => _hasScheduledFrame;
|
||||
bool _hasScheduledFrame = false;
|
||||
|
|
|
@ -52,8 +52,8 @@ abstract class ServicesBinding extends BindingBase {
|
|||
// out the cache of resources that have changed.
|
||||
// TODO(ianh): find a way to only evict affected images, not all images
|
||||
name: 'evict',
|
||||
getter: () => '',
|
||||
setter: (String value) {
|
||||
getter: () async => '',
|
||||
setter: (String value) async {
|
||||
rootBundle.evict(value);
|
||||
imageCache.clear();
|
||||
}
|
||||
|
|
|
@ -76,28 +76,30 @@ abstract class WidgetsBinding extends BindingBase implements GestureBinding, Ren
|
|||
|
||||
registerSignalServiceExtension(
|
||||
name: 'debugDumpApp',
|
||||
callback: debugDumpApp
|
||||
callback: () { debugDumpApp(); return debugPrintDone; }
|
||||
);
|
||||
|
||||
registerBoolServiceExtension(
|
||||
name: 'showPerformanceOverlay',
|
||||
getter: () => WidgetsApp.showPerformanceOverlayOverride,
|
||||
getter: () => new Future<bool>.value(WidgetsApp.showPerformanceOverlayOverride),
|
||||
setter: (bool value) {
|
||||
if (WidgetsApp.showPerformanceOverlayOverride == value)
|
||||
return;
|
||||
return new Future<Null>.value();
|
||||
WidgetsApp.showPerformanceOverlayOverride = value;
|
||||
buildOwner.reassemble(renderViewElement);
|
||||
return endOfFrame;
|
||||
}
|
||||
);
|
||||
|
||||
registerBoolServiceExtension(
|
||||
name: 'debugAllowBanner',
|
||||
getter: () => WidgetsApp.debugAllowBannerOverride,
|
||||
getter: () => new Future<bool>.value(WidgetsApp.debugAllowBannerOverride),
|
||||
setter: (bool value) {
|
||||
if (WidgetsApp.debugAllowBannerOverride == value)
|
||||
return;
|
||||
return new Future<Null>.value();
|
||||
WidgetsApp.debugAllowBannerOverride = value;
|
||||
buildOwner.reassemble(renderViewElement);
|
||||
return endOfFrame;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -365,12 +367,12 @@ abstract class WidgetsBinding extends BindingBase implements GestureBinding, Ren
|
|||
}
|
||||
|
||||
@override
|
||||
void reassembleApplication() {
|
||||
Future<Null> reassembleApplication() {
|
||||
_needToReportFirstFrame = true;
|
||||
preventThisFrameFromBeingReportedAsFirstFrame();
|
||||
if (renderViewElement != null)
|
||||
buildOwner.reassemble(renderViewElement);
|
||||
super.reassembleApplication();
|
||||
return super.reassembleApplication();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -432,14 +432,12 @@ class AndroidDevice extends Device {
|
|||
bool get supportsScreenshot => true;
|
||||
|
||||
@override
|
||||
Future<bool> takeScreenshot(File outputFile) {
|
||||
Future<Null> takeScreenshot(File outputFile) {
|
||||
const String remotePath = '/data/local/tmp/flutter_screenshot.png';
|
||||
|
||||
runCheckedSync(adbCommandForDevice(<String>['shell', 'screencap', '-p', remotePath]));
|
||||
runCheckedSync(adbCommandForDevice(<String>['pull', remotePath, outputFile.path]));
|
||||
runCheckedSync(adbCommandForDevice(<String>['shell', 'rm', remotePath]));
|
||||
|
||||
return new Future<bool>.value(true);
|
||||
return new Future<Null>.value();
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -85,8 +85,7 @@ class ScreenshotCommand extends FlutterCommand {
|
|||
Future<Null> runScreenshot(File outputFile) async {
|
||||
outputFile ??= getUniqueFile(fs.currentDirectory, 'flutter', 'png');
|
||||
try {
|
||||
if (!await device.takeScreenshot(outputFile))
|
||||
throwToolExit('Screenshot failed');
|
||||
await device.takeScreenshot(outputFile);
|
||||
} catch (error) {
|
||||
throwToolExit('Error taking screenshot: $error');
|
||||
}
|
||||
|
|
|
@ -181,8 +181,10 @@ class ServiceProtocolDevFSOperations implements DevFSOperations {
|
|||
|
||||
@override
|
||||
Future<dynamic> destroy(String fsName) async {
|
||||
await vmService.vm.invokeRpcRaw('_deleteDevFS',
|
||||
<String, dynamic> { 'fsName': fsName });
|
||||
await vmService.vm.invokeRpcRaw(
|
||||
'_deleteDevFS',
|
||||
params: <String, dynamic> { 'fsName': fsName },
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -195,14 +197,16 @@ class ServiceProtocolDevFSOperations implements DevFSOperations {
|
|||
}
|
||||
String fileContents = BASE64.encode(bytes);
|
||||
try {
|
||||
return await vmService.vm.invokeRpcRaw('_writeDevFSFile',
|
||||
<String, dynamic> {
|
||||
'fsName': fsName,
|
||||
'path': devicePath,
|
||||
'fileContents': fileContents
|
||||
});
|
||||
} catch (e) {
|
||||
printTrace('DevFS: Failed to write $devicePath: $e');
|
||||
return await vmService.vm.invokeRpcRaw(
|
||||
'_writeDevFSFile',
|
||||
params: <String, dynamic> {
|
||||
'fsName': fsName,
|
||||
'path': devicePath,
|
||||
'fileContents': fileContents
|
||||
},
|
||||
);
|
||||
} catch (error) {
|
||||
printTrace('DevFS: Failed to write $devicePath: $error');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -219,7 +219,7 @@ abstract class Device {
|
|||
|
||||
bool get supportsScreenshot => false;
|
||||
|
||||
Future<bool> takeScreenshot(File outputFile) => new Future<bool>.error('unimplemented');
|
||||
Future<Null> takeScreenshot(File outputFile) => new Future<Null>.error('unimplemented');
|
||||
|
||||
/// Find the apps that are currently running on this device.
|
||||
Future<List<DiscoveredApp>> discoverApps() =>
|
||||
|
|
|
@ -365,12 +365,11 @@ class IOSDevice extends Device {
|
|||
bool get supportsScreenshot => false;
|
||||
|
||||
@override
|
||||
Future<bool> takeScreenshot(File outputFile) {
|
||||
Future<Null> takeScreenshot(File outputFile) {
|
||||
// We could use idevicescreenshot here (installed along with the brew
|
||||
// ideviceinstaller tools). It however requires a developer disk image on
|
||||
// the device.
|
||||
|
||||
return new Future<bool>.value(false);
|
||||
return new Future<Null>.error('Taking screenshots is not supported on iOS devices. Consider using a simulator instead.');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -587,7 +587,7 @@ class IOSSimulator extends Device {
|
|||
bool get supportsScreenshot => true;
|
||||
|
||||
@override
|
||||
Future<bool> takeScreenshot(File outputFile) async {
|
||||
Future<Null> takeScreenshot(File outputFile) async {
|
||||
Directory desktopDir = fs.directory(path.join(homeDirPath, 'Desktop'));
|
||||
|
||||
// 'Simulator Screen Shot Mar 25, 2016, 2.59.43 PM.png'
|
||||
|
@ -621,8 +621,6 @@ class IOSSimulator extends Device {
|
|||
File shot = shots.first;
|
||||
outputFile.writeAsBytesSync(shot.readAsBytesSync());
|
||||
shot.delete();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -108,19 +108,34 @@ abstract class ResidentRunner {
|
|||
}
|
||||
|
||||
Future<Null> _screenshot() async {
|
||||
Status status = logger.startProgress('Taking screenshot...');
|
||||
File outputFile = getUniqueFile(fs.currentDirectory, 'flutter', 'png');
|
||||
try {
|
||||
if (vmService != null)
|
||||
await vmService.vm.refreshViews();
|
||||
if (isRunningDebug)
|
||||
await currentView.uiIsolate.flutterDebugAllowBanner(false);
|
||||
if (!await device.takeScreenshot(outputFile))
|
||||
printError('Error taking screenshot.');
|
||||
if (isRunningDebug)
|
||||
await currentView.uiIsolate.flutterDebugAllowBanner(true);
|
||||
try {
|
||||
if (isRunningDebug)
|
||||
await currentView.uiIsolate.flutterDebugAllowBanner(false);
|
||||
} catch (error) {
|
||||
status.stop();
|
||||
printError(error);
|
||||
}
|
||||
try {
|
||||
await device.takeScreenshot(outputFile);
|
||||
} finally {
|
||||
try {
|
||||
if (isRunningDebug)
|
||||
await currentView.uiIsolate.flutterDebugAllowBanner(true);
|
||||
} catch (error) {
|
||||
status.stop();
|
||||
printError(error);
|
||||
}
|
||||
}
|
||||
int sizeKB = (await outputFile.length()) ~/ 1024;
|
||||
status.stop();
|
||||
printStatus('Screenshot written to ${path.relative(outputFile.path)} (${sizeKB}kB).');
|
||||
} catch (error) {
|
||||
status.stop();
|
||||
printError('Error taking screenshot: $error');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ class VMService {
|
|||
///
|
||||
/// Requests made via the returns [VMService] time out after [requestTimeout]
|
||||
/// amount of time, which is [kDefaultRequestTimeout] by default.
|
||||
static Future<VMService> connect(Uri httpUri, {Duration requestTimeout: kDefaultRequestTimeout}) async {
|
||||
static Future<VMService> connect(Uri httpUri, { Duration requestTimeout: kDefaultRequestTimeout }) async {
|
||||
Uri wsUri = httpUri.replace(scheme: 'ws', path: path.join(httpUri.path, 'ws'));
|
||||
WebSocket ws;
|
||||
try {
|
||||
|
@ -264,7 +264,7 @@ abstract class ServiceObject {
|
|||
Map<String, dynamic> params = <String, dynamic>{
|
||||
'objectId': id,
|
||||
};
|
||||
return _owner.isolate.invokeRpcRaw('getObject', params);
|
||||
return _owner.isolate.invokeRpcRaw('getObject', params: params);
|
||||
}
|
||||
|
||||
Future<ServiceObject> _inProgressReload;
|
||||
|
@ -437,7 +437,7 @@ class VM extends ServiceObjectOwner {
|
|||
|
||||
@override
|
||||
Future<Map<String, dynamic>> _fetchDirect() async {
|
||||
return invokeRpcRaw('getVM', <String, dynamic> {});
|
||||
return invokeRpcRaw('getVM');
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -567,27 +567,39 @@ class VM extends ServiceObjectOwner {
|
|||
}
|
||||
|
||||
/// Invoke the RPC and return the raw response.
|
||||
Future<Map<String, dynamic>> invokeRpcRaw(
|
||||
String method, [Map<String, dynamic> params, Duration timeout]) async {
|
||||
if (params == null) {
|
||||
params = <String, dynamic>{};
|
||||
}
|
||||
|
||||
///
|
||||
/// If `timeoutFatal` is false, then a timeout will result in a null return
|
||||
/// value. Otherwise, it results in an exception.
|
||||
Future<Map<String, dynamic>> invokeRpcRaw(String method, {
|
||||
Map<String, dynamic> params: const <String, dynamic>{},
|
||||
Duration timeout,
|
||||
bool timeoutFatal: true,
|
||||
}) async {
|
||||
assert(params != null);
|
||||
timeout ??= _vmService._requestTimeout;
|
||||
try {
|
||||
Map<String, dynamic> result = await _vmService.peer
|
||||
.sendRequest(method, params)
|
||||
.timeout(timeout ?? _vmService._requestTimeout);
|
||||
.timeout(timeout);
|
||||
return result;
|
||||
} on TimeoutException catch(_) {
|
||||
printError('Request to Dart VM Service timed out: $method($params)');
|
||||
rethrow;
|
||||
} on TimeoutException {
|
||||
printTrace('Request to Dart VM Service timed out: $method($params)');
|
||||
if (timeoutFatal)
|
||||
throw new TimeoutException('Request to Dart VM Service timed out: $method($params)');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// Invoke the RPC and return a ServiceObject response.
|
||||
Future<ServiceObject> invokeRpc(
|
||||
String method, { Map<String, dynamic> params, Duration timeout }) async {
|
||||
Map<String, dynamic> response = await invokeRpcRaw(method, params, timeout);
|
||||
/// Invoke the RPC and return a [ServiceObject] response.
|
||||
Future<ServiceObject> invokeRpc(String method, {
|
||||
Map<String, dynamic> params: const <String, dynamic>{},
|
||||
Duration timeout,
|
||||
}) async {
|
||||
Map<String, dynamic> response = await invokeRpcRaw(
|
||||
method,
|
||||
params: params,
|
||||
timeout: timeout,
|
||||
);
|
||||
ServiceObject serviceObject = new ServiceObject._fromMap(this, response);
|
||||
if ((serviceObject != null) && (serviceObject._canCache)) {
|
||||
String serviceObjectId = serviceObject.id;
|
||||
|
@ -597,19 +609,13 @@ class VM extends ServiceObjectOwner {
|
|||
}
|
||||
|
||||
/// Create a new development file system on the device.
|
||||
Future<Map<String, dynamic>> createDevFS(String fsName) async {
|
||||
Map<String, dynamic> response =
|
||||
await invokeRpcRaw('_createDevFS', <String, dynamic> {
|
||||
'fsName': fsName
|
||||
});
|
||||
return response;
|
||||
Future<Map<String, dynamic>> createDevFS(String fsName) {
|
||||
return invokeRpcRaw('_createDevFS', params: <String, dynamic> { 'fsName': fsName });
|
||||
}
|
||||
|
||||
/// List the development file system son the device.
|
||||
Future<List<String>> listDevFS() async {
|
||||
Map<String, dynamic> response =
|
||||
await invokeRpcRaw('_listDevFS', <String, dynamic>{});
|
||||
return response['fsNames'];
|
||||
return (await invokeRpcRaw('_listDevFS'))['fsNames'];
|
||||
}
|
||||
|
||||
// Write one file into a file system.
|
||||
|
@ -619,36 +625,36 @@ class VM extends ServiceObjectOwner {
|
|||
}) {
|
||||
assert(path != null);
|
||||
assert(fileContents != null);
|
||||
|
||||
return invokeRpcRaw('_writeDevFSFile', <String, dynamic> {
|
||||
'fsName': fsName,
|
||||
'path': path,
|
||||
'fileContents': BASE64.encode(fileContents)
|
||||
});
|
||||
return invokeRpcRaw(
|
||||
'_writeDevFSFile',
|
||||
params: <String, dynamic>{
|
||||
'fsName': fsName,
|
||||
'path': path,
|
||||
'fileContents': BASE64.encode(fileContents),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// Read one file from a file system.
|
||||
Future<List<int>> readDevFSFile(String fsName, String path) {
|
||||
return invokeRpcRaw('_readDevFSFile', <String, dynamic> {
|
||||
'fsName': fsName,
|
||||
'path': path
|
||||
}).then((Map<String, dynamic> response) {
|
||||
return BASE64.decode(response['fileContents']);
|
||||
});
|
||||
Future<List<int>> readDevFSFile(String fsName, String path) async {
|
||||
Map<String, dynamic> response = await invokeRpcRaw(
|
||||
'_readDevFSFile',
|
||||
params: <String, dynamic>{
|
||||
'fsName': fsName,
|
||||
'path': path,
|
||||
},
|
||||
);
|
||||
return BASE64.decode(response['fileContents']);
|
||||
}
|
||||
|
||||
/// The complete list of a file system.
|
||||
Future<List<String>> listDevFSFiles(String fsName) {
|
||||
return invokeRpcRaw('_listDevFSFiles', <String, dynamic> {
|
||||
'fsName': fsName
|
||||
}).then((Map<String, dynamic> response) {
|
||||
return response['files'];
|
||||
});
|
||||
Future<List<String>> listDevFSFiles(String fsName) async {
|
||||
return (await invokeRpcRaw('_listDevFSFiles', params: <String, dynamic>{ 'fsName': fsName }))['files'];
|
||||
}
|
||||
|
||||
/// Delete an existing file system.
|
||||
Future<Map<String, dynamic>> deleteDevFS(String fsName) {
|
||||
return invokeRpcRaw('_deleteDevFS', <String, dynamic> { 'fsName': fsName });
|
||||
return invokeRpcRaw('_deleteDevFS', params: <String, dynamic>{ 'fsName': fsName });
|
||||
}
|
||||
|
||||
Future<ServiceMap> runInView(String viewId,
|
||||
|
@ -665,20 +671,21 @@ class VM extends ServiceObjectOwner {
|
|||
}
|
||||
|
||||
Future<Map<String, dynamic>> clearVMTimeline() {
|
||||
return invokeRpcRaw('_clearVMTimeline', <String, dynamic>{});
|
||||
return invokeRpcRaw('_clearVMTimeline');
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> setVMTimelineFlags(
|
||||
List<String> recordedStreams) {
|
||||
Future<Map<String, dynamic>> setVMTimelineFlags(List<String> recordedStreams) {
|
||||
assert(recordedStreams != null);
|
||||
|
||||
return invokeRpcRaw('_setVMTimelineFlags', <String, dynamic> {
|
||||
'recordedStreams': recordedStreams
|
||||
});
|
||||
return invokeRpcRaw(
|
||||
'_setVMTimelineFlags',
|
||||
params: <String, dynamic>{
|
||||
'recordedStreams': recordedStreams,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> getVMTimeline() {
|
||||
return invokeRpcRaw('_getVMTimeline', <String, dynamic> {}, kLongRequestTimeout);
|
||||
return invokeRpcRaw('_getVMTimeline', timeout: kLongRequestTimeout);
|
||||
}
|
||||
|
||||
Future<Null> refreshViews() async {
|
||||
|
@ -736,12 +743,15 @@ class Isolate extends ServiceObjectOwner {
|
|||
|
||||
@override
|
||||
Future<Map<String, dynamic>> _fetchDirect() {
|
||||
return invokeRpcRaw('getIsolate', <String, dynamic>{});
|
||||
return invokeRpcRaw('getIsolate');
|
||||
}
|
||||
|
||||
/// Invoke the RPC and return the raw response.
|
||||
Future<Map<String, dynamic>> invokeRpcRaw(
|
||||
String method, [Map<String, dynamic> params, Duration timeout]) {
|
||||
Future<Map<String, dynamic>> invokeRpcRaw(String method, {
|
||||
Map<String, dynamic> params,
|
||||
Duration timeout,
|
||||
bool timeoutFatal: true,
|
||||
}) {
|
||||
// Inject the 'isolateId' parameter.
|
||||
if (params == null) {
|
||||
params = <String, dynamic>{
|
||||
|
@ -750,21 +760,18 @@ class Isolate extends ServiceObjectOwner {
|
|||
} else {
|
||||
params['isolateId'] = id;
|
||||
}
|
||||
return vm.invokeRpcRaw(method, params, timeout);
|
||||
return vm.invokeRpcRaw(method, params: params, timeout: timeout, timeoutFatal: timeoutFatal);
|
||||
}
|
||||
|
||||
/// Invoke the RPC and return a ServiceObject response.
|
||||
Future<ServiceObject> invokeRpc(
|
||||
String method, Map<String, dynamic> params) async {
|
||||
Map<String, dynamic> response = await invokeRpcRaw(method, params);
|
||||
return getFromMap(response);
|
||||
Future<ServiceObject> invokeRpc(String method, Map<String, dynamic> params) async {
|
||||
return getFromMap(await invokeRpcRaw(method, params: params));
|
||||
}
|
||||
|
||||
@override
|
||||
void _update(Map<String, dynamic> map, bool mapIsRef) {
|
||||
if (mapIsRef) {
|
||||
if (mapIsRef)
|
||||
return;
|
||||
}
|
||||
_loaded = true;
|
||||
|
||||
int startTimeMillis = map['startTime'];
|
||||
|
@ -791,7 +798,7 @@ class Isolate extends ServiceObjectOwner {
|
|||
if (packagesPath != null) {
|
||||
arguments['packagesUri'] = packagesPath;
|
||||
}
|
||||
Map<String, dynamic> response = await invokeRpcRaw('_reloadSources', arguments);
|
||||
Map<String, dynamic> response = await invokeRpcRaw('_reloadSources', params: arguments);
|
||||
return response;
|
||||
} on rpc.RpcException catch(e) {
|
||||
return new Future<Map<String, dynamic>>.error(<String, dynamic>{
|
||||
|
@ -807,9 +814,14 @@ class Isolate extends ServiceObjectOwner {
|
|||
// 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, Duration timeout }) async {
|
||||
String method, {
|
||||
Map<String, dynamic> params,
|
||||
Duration timeout,
|
||||
bool timeoutFatal: true,
|
||||
}
|
||||
) async {
|
||||
try {
|
||||
return await invokeRpcRaw(method, params, timeout);
|
||||
return await invokeRpcRaw(method, params: params, timeout: timeout, timeoutFatal: timeoutFatal);
|
||||
} on rpc.RpcException catch (e) {
|
||||
// If an application is not using the framework
|
||||
if (e.code == rpc_error_code.METHOD_NOT_FOUND)
|
||||
|
@ -821,29 +833,42 @@ class Isolate extends ServiceObjectOwner {
|
|||
// Debug dump extension methods.
|
||||
|
||||
Future<Map<String, dynamic>> flutterDebugDumpApp() {
|
||||
return invokeFlutterExtensionRpcRaw('ext.flutter.debugDumpApp');
|
||||
return invokeFlutterExtensionRpcRaw('ext.flutter.debugDumpApp', timeout: kLongRequestTimeout);
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> flutterDebugDumpRenderTree() {
|
||||
return invokeFlutterExtensionRpcRaw('ext.flutter.debugDumpRenderTree');
|
||||
return invokeFlutterExtensionRpcRaw('ext.flutter.debugDumpRenderTree', timeout: kLongRequestTimeout);
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> flutterToggleDebugPaintSizeEnabled() async {
|
||||
Map<String, dynamic> state = await invokeFlutterExtensionRpcRaw('ext.flutter.debugPaint');
|
||||
if (state != null && state.containsKey('enabled') && state['enabled'] is bool)
|
||||
state = await invokeFlutterExtensionRpcRaw('ext.flutter.debugPaint',
|
||||
params: <String, dynamic>{ 'enabled': !state['enabled'] });
|
||||
if (state != null && state.containsKey('enabled') && state['enabled'] is bool) {
|
||||
state = await invokeFlutterExtensionRpcRaw(
|
||||
'ext.flutter.debugPaint',
|
||||
params: <String, dynamic>{ 'enabled': !state['enabled'] },
|
||||
timeout: const Duration(milliseconds: 150),
|
||||
timeoutFatal: false,
|
||||
);
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
Future<Null> flutterDebugAllowBanner(bool show) async {
|
||||
await invokeFlutterExtensionRpcRaw('ext.flutter.debugAllowBanner', params: <String, dynamic>{ 'enabled': show });
|
||||
await invokeFlutterExtensionRpcRaw(
|
||||
'ext.flutter.debugAllowBanner',
|
||||
params: <String, dynamic>{ 'enabled': show },
|
||||
timeout: const Duration(milliseconds: 150),
|
||||
timeoutFatal: false,
|
||||
);
|
||||
}
|
||||
|
||||
// Reload related extension methods.
|
||||
Future<Map<String, dynamic>> flutterReassemble() async {
|
||||
return await invokeFlutterExtensionRpcRaw('ext.flutter.reassemble',
|
||||
timeout: kLongRequestTimeout);
|
||||
return await invokeFlutterExtensionRpcRaw(
|
||||
'ext.flutter.reassemble',
|
||||
timeout: kLongRequestTimeout,
|
||||
timeoutFatal: false,
|
||||
);
|
||||
}
|
||||
|
||||
Future<bool> flutterFrameworkPresent() async {
|
||||
|
@ -856,16 +881,19 @@ class Isolate extends ServiceObjectOwner {
|
|||
|
||||
Future<Map<String, dynamic>> flutterEvictAsset(String assetPath) async {
|
||||
return await invokeFlutterExtensionRpcRaw('ext.flutter.evict',
|
||||
params: <String, dynamic>{
|
||||
'value': assetPath
|
||||
}
|
||||
params: <String, dynamic>{
|
||||
'value': assetPath,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Application control extension methods.
|
||||
Future<Map<String, dynamic>> flutterExit() async {
|
||||
return await invokeFlutterExtensionRpcRaw('ext.flutter.exit').timeout(
|
||||
const Duration(seconds: 2), onTimeout: () => null);
|
||||
return await invokeFlutterExtensionRpcRaw(
|
||||
'ext.flutter.exit',
|
||||
timeout: const Duration(seconds: 2),
|
||||
timeoutFatal: false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -297,8 +297,11 @@ class MockVM implements VM {
|
|||
}
|
||||
|
||||
@override
|
||||
Future<Map<String, dynamic>> invokeRpcRaw(
|
||||
String method, [Map<String, dynamic> params, Duration timeout]) async {
|
||||
Future<Map<String, dynamic>> invokeRpcRaw(String method, {
|
||||
Map<String, dynamic> params: const <String, dynamic>{},
|
||||
Duration timeout,
|
||||
bool timeoutFatal: true,
|
||||
}) async {
|
||||
_service.messages.add('$method $params');
|
||||
return <String, dynamic>{'success': true};
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue