mirror of
https://github.com/flutter/flutter
synced 2024-09-12 21:01:59 +00:00
Started handling messages from background isolates. (#109005)
Started handling messages from background isolates.
This commit is contained in:
parent
d671a834f3
commit
0d19d46b44
|
@ -18,6 +18,7 @@ found in the LICENSE file. -->
|
|||
Application and put your custom class here. -->
|
||||
<application android:name="${applicationName}" android:label="channels" android:icon="@mipmap/ic_launcher">
|
||||
<activity android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
android:launchMode="singleTop"
|
||||
android:theme="@android:style/Theme.Black.NoTitleBar"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||
|
|
|
@ -114,7 +114,18 @@ const UInt8 PAIR = 129;
|
|||
[FlutterMethodChannel methodChannelWithName:@"std-method"
|
||||
binaryMessenger:flutterController
|
||||
codec:[FlutterStandardMethodCodec codecWithReaderWriter:extendedReaderWriter]]];
|
||||
return [super application:application didFinishLaunchingWithOptions:launchOptions];
|
||||
|
||||
[[FlutterBasicMessageChannel
|
||||
messageChannelWithName:@"std-echo"
|
||||
binaryMessenger:flutterController
|
||||
codec:[FlutterStandardMessageCodec
|
||||
codecWithReaderWriter:extendedReaderWriter]]
|
||||
setMessageHandler:^(id message, FlutterReply reply) {
|
||||
reply(message);
|
||||
}];
|
||||
|
||||
return [super application:application
|
||||
didFinishLaunchingWithOptions:launchOptions];
|
||||
}
|
||||
|
||||
- (void)setupMessagingHandshakeOnChannel:(FlutterBasicMessageChannel*)channel {
|
||||
|
|
|
@ -173,6 +173,8 @@ class _TestAppState extends State<TestApp> {
|
|||
() => basicStringMessageToUnknownChannel(),
|
||||
() => basicJsonMessageToUnknownChannel(),
|
||||
() => basicStandardMessageToUnknownChannel(),
|
||||
if (Platform.isIOS)
|
||||
() => basicBackgroundStandardEcho(123),
|
||||
];
|
||||
Future<TestStepResult>? _result;
|
||||
int _step = 0;
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:isolate';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
|
@ -78,6 +79,41 @@ Future<TestStepResult> basicStandardHandshake(dynamic message) async {
|
|||
'Standard >${toString(message)}<', channel, message);
|
||||
}
|
||||
|
||||
Future<void> _basicBackgroundStandardEchoMain(List<Object> args) async {
|
||||
final SendPort sendPort = args[2] as SendPort;
|
||||
final Object message = args[1];
|
||||
final String name = 'Background Echo >${toString(message)}<';
|
||||
const String description =
|
||||
'Uses a platform channel from a background isolate.';
|
||||
try {
|
||||
BackgroundIsolateBinaryMessenger.ensureInitialized(
|
||||
args[0] as RootIsolateToken);
|
||||
const BasicMessageChannel<dynamic> channel = BasicMessageChannel<dynamic>(
|
||||
'std-echo',
|
||||
ExtendedStandardMessageCodec(),
|
||||
);
|
||||
final Object response = await channel.send(message) as Object;
|
||||
|
||||
final TestStatus testStatus = TestStepResult.deepEquals(message, response)
|
||||
? TestStatus.ok
|
||||
: TestStatus.failed;
|
||||
sendPort.send(TestStepResult(name, description, testStatus));
|
||||
} catch (ex) {
|
||||
sendPort.send(TestStepResult(name, description, TestStatus.failed,
|
||||
error: ex.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
Future<TestStepResult> basicBackgroundStandardEcho(Object message) async {
|
||||
final ReceivePort receivePort = ReceivePort();
|
||||
Isolate.spawn(_basicBackgroundStandardEchoMain, <Object>[
|
||||
ServicesBinding.instance.rootIsolateToken!,
|
||||
message,
|
||||
receivePort.sendPort,
|
||||
]);
|
||||
return await receivePort.first as TestStepResult;
|
||||
}
|
||||
|
||||
Future<TestStepResult> basicBinaryMessageToUnknownChannel() async {
|
||||
const BasicMessageChannel<ByteData?> channel =
|
||||
BasicMessageChannel<ByteData?>(
|
||||
|
|
|
@ -90,6 +90,8 @@ class TestStepResult {
|
|||
],
|
||||
);
|
||||
}
|
||||
|
||||
static bool deepEquals(dynamic a, dynamic b) => _deepEquals(a, b);
|
||||
}
|
||||
|
||||
Future<TestStepResult> resultOfHandshake(
|
||||
|
|
|
@ -82,6 +82,21 @@ mixin ServicesBinding on BindingBase, SchedulerBinding {
|
|||
BinaryMessenger get defaultBinaryMessenger => _defaultBinaryMessenger;
|
||||
late final BinaryMessenger _defaultBinaryMessenger;
|
||||
|
||||
/// A token that represents the root isolate, used for coordinating with background
|
||||
/// isolates.
|
||||
///
|
||||
/// This property is primarily intended for use with
|
||||
/// [BackgroundIsolateBinaryMessenger.ensureInitialized], which takes a
|
||||
/// [RootIsolateToken] as its argument. The value `null` is returned when
|
||||
/// executed from background isolates.
|
||||
ui.RootIsolateToken? get rootIsolateToken => ui.RootIsolateToken.instance;
|
||||
|
||||
/// Returns `true` if executed on a background (non-root) isolate.
|
||||
///
|
||||
/// The value `false` will always be returned on web since there is no notion
|
||||
/// of root/background isolates on the web.
|
||||
bool get useBackgroundIsolateBinaryMessenger => !kIsWeb && rootIsolateToken == null;
|
||||
|
||||
/// The low level buffering and dispatch mechanism for messages sent by
|
||||
/// plugins on the engine side to their corresponding plugin code on
|
||||
/// the framework side.
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
import 'dart:async';
|
||||
import 'dart:developer';
|
||||
import 'dart:isolate' show ReceivePort;
|
||||
import 'dart:ui' as ui;
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
|
@ -13,6 +15,7 @@ import 'debug.dart' show debugProfilePlatformChannels;
|
|||
import 'message_codec.dart';
|
||||
import 'message_codecs.dart';
|
||||
|
||||
export 'dart:ui' show RootIsolateToken;
|
||||
export 'binary_messenger.dart' show BinaryMessenger;
|
||||
export 'message_codec.dart' show MessageCodec, MethodCall, MethodCodec;
|
||||
|
||||
|
@ -123,6 +126,93 @@ void _debugRecordDownStream(String channelTypeName, String name,
|
|||
_debugLaunchProfilePlatformChannels();
|
||||
}
|
||||
|
||||
/// A [BinaryMessenger] for use on background (non-root) isolates.
|
||||
class BackgroundIsolateBinaryMessenger extends BinaryMessenger {
|
||||
BackgroundIsolateBinaryMessenger._();
|
||||
|
||||
final ReceivePort _receivePort = ReceivePort();
|
||||
final Map<int, Completer<ByteData?>> _completers = <int, Completer<ByteData?>>{};
|
||||
int _messageCount = 0;
|
||||
|
||||
/// The existing instance of this class, if any.
|
||||
///
|
||||
/// Throws if [ensureInitialized] has not been called at least once.
|
||||
static BinaryMessenger get instance {
|
||||
if (_instance == null) {
|
||||
throw StateError(
|
||||
'The BackgroundIsolateBinaryMessenger.instance value is invalid '
|
||||
'until BackgroundIsolateBinaryMessenger.ensureInitialized is '
|
||||
'executed.');
|
||||
}
|
||||
return _instance!;
|
||||
}
|
||||
|
||||
static BinaryMessenger? _instance;
|
||||
|
||||
/// Ensures that [BackgroundIsolateBinaryMessenger.instance] has been initialized.
|
||||
///
|
||||
/// The argument should be the value obtained from [ServicesBinding.rootIsolateToken]
|
||||
/// on the root isolate.
|
||||
///
|
||||
/// This function is idempotent (calling it multiple times is harmless but has no effect).
|
||||
static void ensureInitialized(ui.RootIsolateToken token) {
|
||||
if (_instance == null) {
|
||||
ui.PlatformDispatcher.instance.registerBackgroundIsolate(token);
|
||||
final BackgroundIsolateBinaryMessenger portBinaryMessenger = BackgroundIsolateBinaryMessenger._();
|
||||
_instance = portBinaryMessenger;
|
||||
portBinaryMessenger._receivePort.listen((dynamic message) {
|
||||
try {
|
||||
final List<dynamic> args = message as List<dynamic>;
|
||||
final int identifier = args[0] as int;
|
||||
final Uint8List bytes = args[1] as Uint8List;
|
||||
final ByteData byteData = ByteData.sublistView(bytes);
|
||||
portBinaryMessenger._completers.remove(identifier)!.complete(byteData);
|
||||
} catch (exception, stack) {
|
||||
FlutterError.reportError(FlutterErrorDetails(
|
||||
exception: exception,
|
||||
stack: stack,
|
||||
library: 'services library',
|
||||
context:
|
||||
ErrorDescription('during a platform message response callback'),
|
||||
));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> handlePlatformMessage(String channel, ByteData? data, ui.PlatformMessageResponseCallback? callback) {
|
||||
throw UnimplementedError('handlePlatformMessage is deprecated.');
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ByteData?>? send(String channel, ByteData? message) {
|
||||
final Completer<ByteData?> completer = Completer<ByteData?>();
|
||||
_messageCount += 1;
|
||||
final int messageIdentifier = _messageCount;
|
||||
_completers[messageIdentifier] = completer;
|
||||
ui.PlatformDispatcher.instance.sendPortPlatformMessage(
|
||||
channel,
|
||||
message,
|
||||
messageIdentifier,
|
||||
_receivePort.sendPort,
|
||||
);
|
||||
return completer.future;
|
||||
}
|
||||
|
||||
@override
|
||||
void setMessageHandler(String channel, MessageHandler? handler) {
|
||||
throw UnsupportedError(
|
||||
'Background isolates do not support setMessageHandler(). Messages from the host platform always go to the root isolate.');
|
||||
}
|
||||
}
|
||||
|
||||
BinaryMessenger _findBinaryMessenger() {
|
||||
return ServicesBinding.instance.useBackgroundIsolateBinaryMessenger
|
||||
? BackgroundIsolateBinaryMessenger.instance
|
||||
: ServicesBinding.instance.defaultBinaryMessenger;
|
||||
}
|
||||
|
||||
/// A named channel for communicating with platform plugins using asynchronous
|
||||
/// message passing.
|
||||
///
|
||||
|
@ -160,10 +250,14 @@ class BasicMessageChannel<T> {
|
|||
/// The message codec used by this channel, not null.
|
||||
final MessageCodec<T> codec;
|
||||
|
||||
/// The messenger which sends the bytes for this channel, not null.
|
||||
/// The messenger which sends the bytes for this channel.
|
||||
///
|
||||
/// On the root isolate or web, this defaults to the
|
||||
/// [ServicesBinding.defaultBinaryMessenger]. In other contexts the default
|
||||
/// value is a [BackgroundIsolateBinaryMessenger] from
|
||||
/// [BackgroundIsolateBinaryMessenger.ensureInitialized].
|
||||
BinaryMessenger get binaryMessenger {
|
||||
final BinaryMessenger result =
|
||||
_binaryMessenger ?? ServicesBinding.instance.defaultBinaryMessenger;
|
||||
final BinaryMessenger result = _binaryMessenger ?? _findBinaryMessenger();
|
||||
return !kReleaseMode && debugProfilePlatformChannels
|
||||
? _debugBinaryMessengers[this] ??= _ProfiledBinaryMessenger(
|
||||
// ignore: no_runtimetype_tostring
|
||||
|
@ -246,12 +340,14 @@ class MethodChannel {
|
|||
/// The message codec used by this channel, not null.
|
||||
final MethodCodec codec;
|
||||
|
||||
/// The messenger used by this channel to send platform messages.
|
||||
/// The messenger which sends the bytes for this channel.
|
||||
///
|
||||
/// The messenger may not be null.
|
||||
/// On the root isolate or web, this defaults to the
|
||||
/// [ServicesBinding.defaultBinaryMessenger]. In other contexts the default
|
||||
/// value is a [BackgroundIsolateBinaryMessenger] from
|
||||
/// [BackgroundIsolateBinaryMessenger.ensureInitialized].
|
||||
BinaryMessenger get binaryMessenger {
|
||||
final BinaryMessenger result =
|
||||
_binaryMessenger ?? ServicesBinding.instance.defaultBinaryMessenger;
|
||||
final BinaryMessenger result = _binaryMessenger ?? _findBinaryMessenger();
|
||||
return !kReleaseMode && debugProfilePlatformChannels
|
||||
? _debugBinaryMessengers[this] ??= _ProfiledBinaryMessenger(
|
||||
// ignore: no_runtimetype_tostring
|
||||
|
@ -600,8 +696,14 @@ class EventChannel {
|
|||
/// The message codec used by this channel, not null.
|
||||
final MethodCodec codec;
|
||||
|
||||
/// The messenger used by this channel to send platform messages, not null.
|
||||
BinaryMessenger get binaryMessenger => _binaryMessenger ?? ServicesBinding.instance.defaultBinaryMessenger;
|
||||
/// The messenger which sends the bytes for this channel.
|
||||
///
|
||||
/// On the root isolate or web, this defaults to the
|
||||
/// [ServicesBinding.defaultBinaryMessenger]. In other contexts the default
|
||||
/// value is a [BackgroundIsolateBinaryMessenger] from
|
||||
/// [BackgroundIsolateBinaryMessenger.ensureInitialized].
|
||||
BinaryMessenger get binaryMessenger =>
|
||||
_binaryMessenger ?? _findBinaryMessenger();
|
||||
final BinaryMessenger? _binaryMessenger;
|
||||
|
||||
/// Sets up a broadcast stream for receiving events on this channel.
|
||||
|
|
|
@ -104,6 +104,9 @@ mixin TestDefaultBinaryMessengerBinding on BindingBase, ServicesBinding {
|
|||
TestDefaultBinaryMessenger createBinaryMessenger() {
|
||||
return TestDefaultBinaryMessenger(super.createBinaryMessenger());
|
||||
}
|
||||
|
||||
@override
|
||||
bool get useBackgroundIsolateBinaryMessenger => false;
|
||||
}
|
||||
|
||||
/// Base class for bindings used by widgets library tests.
|
||||
|
|
Loading…
Reference in a new issue