iOS Background Platform Channels (flutter/engine#29665)

This commit is contained in:
gaaclarke
2021-11-19 14:14:45 -08:00
committed by GitHub
parent a610124ea5
commit 7ab226eaf4
17 changed files with 618 additions and 123 deletions

View File

@@ -1157,8 +1157,6 @@ FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/connection_col
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/connection_collection_test.mm
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.h
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.mm
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/platform_message_router.h
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/platform_message_router.mm
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/profiler_metrics_ios.h
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/profiler_metrics_ios.mm
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.h
@@ -1190,6 +1188,9 @@ FILE: ../../../flutter/shell/platform/darwin/ios/ios_surface_software.h
FILE: ../../../flutter/shell/platform/darwin/ios/ios_surface_software.mm
FILE: ../../../flutter/shell/platform/darwin/ios/ios_switchable_gl_context.h
FILE: ../../../flutter/shell/platform/darwin/ios/ios_switchable_gl_context.mm
FILE: ../../../flutter/shell/platform/darwin/ios/platform_message_handler_ios.h
FILE: ../../../flutter/shell/platform/darwin/ios/platform_message_handler_ios.mm
FILE: ../../../flutter/shell/platform/darwin/ios/platform_message_handler_ios_test.mm
FILE: ../../../flutter/shell/platform/darwin/ios/platform_view_ios.h
FILE: ../../../flutter/shell/platform/darwin/ios/platform_view_ios.mm
FILE: ../../../flutter/shell/platform/darwin/ios/rendering_api_selection.h

View File

@@ -31,6 +31,8 @@ typedef void (^FlutterBinaryMessageHandler)(NSData* _Nullable message, FlutterBi
typedef int64_t FlutterBinaryMessengerConnection;
@protocol FlutterTaskQueue;
/**
* A facility for communicating with the Flutter side using asynchronous message
* passing with binary messages.
@@ -44,6 +46,16 @@ typedef int64_t FlutterBinaryMessengerConnection;
*/
FLUTTER_DARWIN_EXPORT
@protocol FlutterBinaryMessenger <NSObject>
/// TODO(gaaclarke): Remove optional when macos supports Background Platform Channels.
@optional
- (NSObject<FlutterTaskQueue>*)makeBackgroundTaskQueue;
- (FlutterBinaryMessengerConnection)
setMessageHandlerOnChannel:(NSString*)channel
binaryMessageHandler:(FlutterBinaryMessageHandler _Nullable)handler
taskQueue:(NSObject<FlutterTaskQueue>* _Nullable)taskQueue;
@required
/**
* Sends a binary message to the Flutter side on the specified channel, expecting
* no reply.

View File

@@ -8,6 +8,8 @@
#import "FlutterBinaryMessenger.h"
#import "FlutterCodecs.h"
@protocol FlutterTaskQueue;
NS_ASSUME_NONNULL_BEGIN
/**
* A message reply callback.
@@ -24,7 +26,8 @@ typedef void (^FlutterReply)(id _Nullable reply);
* asynchronous replies back to Flutter.
*
* @param message The message.
* @param callback A callback for submitting a reply to the sender.
* @param callback A callback for submitting a reply to the sender which can be invoked from any
* thread.
*/
typedef void (^FlutterMessageHandler)(id _Nullable message, FlutterReply callback);
@@ -88,6 +91,27 @@ FLUTTER_DARWIN_EXPORT
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
codec:(NSObject<FlutterMessageCodec>*)codec;
/**
* Initializes a `FlutterBasicMessageChannel` with the specified name, binary
* messenger, and message codec.
*
* The channel name logically identifies the channel; identically named channels
* interfere with each other's communication.
*
* The binary messenger is a facility for sending raw, binary messages to the
* Flutter side. This protocol is implemented by `FlutterEngine` and `FlutterViewController`.
*
* @param name The channel name.
* @param messenger The binary messenger.
* @param codec The message codec.
* @param taskQueue The FlutterTaskQueue that executes the handler (see
-[FlutterBinaryMessenger makeBackgroundTaskQueue]).
*/
- (instancetype)initWithName:(NSString*)name
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
codec:(NSObject<FlutterMessageCodec>*)codec
taskQueue:(NSObject<FlutterTaskQueue>* _Nullable)taskQueue;
/**
* Sends the specified message to the Flutter side, ignoring any reply.
*
@@ -142,7 +166,7 @@ typedef void (^FlutterResult)(id _Nullable result);
* Invoke the callback with a `FlutterError` to indicate that the call failed.
* Invoke the callback with `FlutterMethodNotImplemented` to indicate that the
* method was unknown. Any other values, including `nil`, are interpreted as
* successful results.
* successful results. This can be invoked from any thread.
*/
typedef void (^FlutterMethodCallHandler)(FlutterMethodCall* call, FlutterResult result);
@@ -213,6 +237,27 @@ FLUTTER_DARWIN_EXPORT
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
codec:(NSObject<FlutterMethodCodec>*)codec;
/**
* Initializes a `FlutterMethodChannel` with the specified name, binary messenger,
* method codec, and task queue.
*
* The channel name logically identifies the channel; identically named channels
* interfere with each other's communication.
*
* The binary messenger is a facility for sending raw, binary messages to the
* Flutter side. This protocol is implemented by `FlutterEngine` and `FlutterViewController`.
*
* @param name The channel name.
* @param messenger The binary messenger.
* @param codec The method codec.
* @param taskQueue The FlutterTaskQueue that executes the handler (see
-[FlutterBinaryMessenger makeBackgroundTaskQueue]).
*/
- (instancetype)initWithName:(NSString*)name
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
codec:(NSObject<FlutterMethodCodec>*)codec
taskQueue:(NSObject<FlutterTaskQueue>* _Nullable)taskQueue;
// clang-format off
/**
* Invokes the specified Flutter method with the specified arguments, expecting
@@ -371,6 +416,27 @@ FLUTTER_DARWIN_EXPORT
- (instancetype)initWithName:(NSString*)name
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
codec:(NSObject<FlutterMethodCodec>*)codec;
/**
* Initializes a `FlutterEventChannel` with the specified name, binary messenger,
* method codec and task queue.
*
* The channel name logically identifies the channel; identically named channels
* interfere with each other's communication.
*
* The binary messenger is a facility for sending raw, binary messages to the
* Flutter side. This protocol is implemented by `FlutterEngine` and `FlutterViewController`.
*
* @param name The channel name.
* @param messenger The binary messenger.
* @param codec The method codec.
* @param taskQueue The FlutterTaskQueue that executes the handler (see
-[FlutterBinaryMessenger makeBackgroundTaskQueue]).
*/
- (instancetype)initWithName:(NSString*)name
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
codec:(NSObject<FlutterMethodCodec>*)codec
taskQueue:(NSObject<FlutterTaskQueue>* _Nullable)taskQueue;
/**
* Registers a handler for stream setup requests from the Flutter side.
*

View File

@@ -16,11 +16,30 @@ static void ResizeChannelBuffer(NSObject<FlutterBinaryMessenger>* binaryMessenge
[binaryMessenger sendOnChannel:FlutterChannelBuffersChannel message:message];
}
static FlutterBinaryMessengerConnection SetMessageHandler(
NSObject<FlutterBinaryMessenger>* messenger,
NSString* name,
FlutterBinaryMessageHandler handler,
NSObject<FlutterTaskQueue>* taskQueue) {
if (taskQueue) {
NSCAssert([messenger respondsToSelector:@selector(setMessageHandlerOnChannel:
binaryMessageHandler:taskQueue:)],
@"");
return [messenger setMessageHandlerOnChannel:name
binaryMessageHandler:handler
taskQueue:taskQueue];
} else {
return [messenger setMessageHandlerOnChannel:name binaryMessageHandler:handler];
}
}
////////////////////////////////////////////////////////////////////////////////
@implementation FlutterBasicMessageChannel {
NSObject<FlutterBinaryMessenger>* _messenger;
NSString* _name;
NSObject<FlutterMessageCodec>* _codec;
FlutterBinaryMessengerConnection _connection;
NSObject<FlutterTaskQueue>* _taskQueue;
}
+ (instancetype)messageChannelWithName:(NSString*)name
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger {
@@ -40,11 +59,20 @@ static void ResizeChannelBuffer(NSObject<FlutterBinaryMessenger>* binaryMessenge
- (instancetype)initWithName:(NSString*)name
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
codec:(NSObject<FlutterMessageCodec>*)codec {
self = [self initWithName:name binaryMessenger:messenger codec:codec taskQueue:nil];
return self;
}
- (instancetype)initWithName:(NSString*)name
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
codec:(NSObject<FlutterMessageCodec>*)codec
taskQueue:(NSObject<FlutterTaskQueue>*)taskQueue {
self = [super init];
NSAssert(self, @"Super init cannot be nil");
_name = [name retain];
_messenger = [messenger retain];
_codec = [codec retain];
_taskQueue = [taskQueue retain];
return self;
}
@@ -52,6 +80,7 @@ static void ResizeChannelBuffer(NSObject<FlutterBinaryMessenger>* binaryMessenge
[_name release];
[_messenger release];
[_codec release];
[_taskQueue release];
[super dealloc];
}
@@ -85,7 +114,7 @@ static void ResizeChannelBuffer(NSObject<FlutterBinaryMessenger>* binaryMessenge
callback([codec encode:reply]);
});
};
_connection = [_messenger setMessageHandlerOnChannel:_name binaryMessageHandler:messageHandler];
_connection = SetMessageHandler(_messenger, _name, messageHandler, _taskQueue);
}
- (void)resizeChannelBuffer:(NSInteger)newSize {
@@ -96,6 +125,7 @@ static void ResizeChannelBuffer(NSObject<FlutterBinaryMessenger>* binaryMessenge
#pragma mark - Method channel
////////////////////////////////////////////////////////////////////////////////
@implementation FlutterError
+ (instancetype)errorWithCode:(NSString*)code message:(NSString*)message details:(id)details {
return [[[FlutterError alloc] initWithCode:code message:message details:details] autorelease];
@@ -136,6 +166,7 @@ static void ResizeChannelBuffer(NSObject<FlutterBinaryMessenger>* binaryMessenge
}
@end
////////////////////////////////////////////////////////////////////////////////
@implementation FlutterMethodCall
+ (instancetype)methodCallWithMethodName:(NSString*)method arguments:(id)arguments {
return [[[FlutterMethodCall alloc] initWithMethodName:method arguments:arguments] autorelease];
@@ -175,11 +206,13 @@ static void ResizeChannelBuffer(NSObject<FlutterBinaryMessenger>* binaryMessenge
NSObject const* FlutterMethodNotImplemented = [[NSObject alloc] init];
////////////////////////////////////////////////////////////////////////////////
@implementation FlutterMethodChannel {
NSObject<FlutterBinaryMessenger>* _messenger;
NSString* _name;
NSObject<FlutterMethodCodec>* _codec;
FlutterBinaryMessengerConnection _connection;
NSObject<FlutterTaskQueue>* _taskQueue;
}
+ (instancetype)methodChannelWithName:(NSString*)name
@@ -198,11 +231,19 @@ NSObject const* FlutterMethodNotImplemented = [[NSObject alloc] init];
- (instancetype)initWithName:(NSString*)name
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
codec:(NSObject<FlutterMethodCodec>*)codec {
self = [self initWithName:name binaryMessenger:messenger codec:codec taskQueue:nil];
return self;
}
- (instancetype)initWithName:(NSString*)name
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
codec:(NSObject<FlutterMethodCodec>*)codec
taskQueue:(NSObject<FlutterTaskQueue>*)taskQueue {
self = [super init];
NSAssert(self, @"Super init cannot be nil");
_name = [name retain];
_messenger = [messenger retain];
_codec = [codec retain];
_taskQueue = [taskQueue retain];
return self;
}
@@ -210,6 +251,7 @@ NSObject const* FlutterMethodNotImplemented = [[NSObject alloc] init];
[_name release];
[_messenger release];
[_codec release];
[_taskQueue release];
[super dealloc];
}
@@ -256,7 +298,7 @@ NSObject const* FlutterMethodNotImplemented = [[NSObject alloc] init];
}
});
};
_connection = [_messenger setMessageHandlerOnChannel:_name binaryMessageHandler:messageHandler];
_connection = SetMessageHandler(_messenger, _name, messageHandler, _taskQueue);
}
- (void)resizeChannelBuffer:(NSInteger)newSize {
@@ -269,10 +311,13 @@ NSObject const* FlutterMethodNotImplemented = [[NSObject alloc] init];
NSObject const* FlutterEndOfEventStream = [[NSObject alloc] init];
////////////////////////////////////////////////////////////////////////////////
@implementation FlutterEventChannel {
NSObject<FlutterBinaryMessenger>* _messenger;
NSString* _name;
NSObject<FlutterMethodCodec>* _codec;
NSObject<FlutterTaskQueue>* _taskQueue;
FlutterBinaryMessengerConnection _connection;
}
+ (instancetype)eventChannelWithName:(NSString*)name
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger {
@@ -290,11 +335,19 @@ NSObject const* FlutterEndOfEventStream = [[NSObject alloc] init];
- (instancetype)initWithName:(NSString*)name
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
codec:(NSObject<FlutterMethodCodec>*)codec {
return [self initWithName:name binaryMessenger:messenger codec:codec taskQueue:nil];
}
- (instancetype)initWithName:(NSString*)name
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
codec:(NSObject<FlutterMethodCodec>*)codec
taskQueue:(NSObject<FlutterTaskQueue>* _Nullable)taskQueue {
self = [super init];
NSAssert(self, @"Super init cannot be nil");
_name = [name retain];
_messenger = [messenger retain];
_codec = [codec retain];
_taskQueue = [taskQueue retain];
return self;
}
@@ -302,13 +355,16 @@ NSObject const* FlutterEndOfEventStream = [[NSObject alloc] init];
[_name release];
[_codec release];
[_messenger release];
[_taskQueue release];
[super dealloc];
}
static void SetStreamHandlerMessageHandlerOnChannel(NSObject<FlutterStreamHandler>* handler,
NSString* name,
NSObject<FlutterBinaryMessenger>* messenger,
NSObject<FlutterMethodCodec>* codec) {
static FlutterBinaryMessengerConnection SetStreamHandlerMessageHandlerOnChannel(
NSObject<FlutterStreamHandler>* handler,
NSString* name,
NSObject<FlutterBinaryMessenger>* messenger,
NSObject<FlutterMethodCodec>* codec,
NSObject<FlutterTaskQueue>* taskQueue) {
__block FlutterEventSink currentSink = nil;
FlutterBinaryMessageHandler messageHandler = ^(NSData* message, FlutterBinaryReply callback) {
FlutterMethodCall* call = [codec decodeMethodCall:message];
@@ -354,14 +410,16 @@ static void SetStreamHandlerMessageHandlerOnChannel(NSObject<FlutterStreamHandle
callback(nil);
}
};
[messenger setMessageHandlerOnChannel:name binaryMessageHandler:messageHandler];
return SetMessageHandler(messenger, name, messageHandler, taskQueue);
}
- (void)setStreamHandler:(NSObject<FlutterStreamHandler>*)handler {
if (!handler) {
[_messenger setMessageHandlerOnChannel:_name binaryMessageHandler:nil];
[_messenger cleanUpConnection:_connection];
_connection = 0;
return;
}
SetStreamHandlerMessageHandlerOnChannel(handler, _name, _messenger, _codec);
_connection =
SetStreamHandlerMessageHandlerOnChannel(handler, _name, _messenger, _codec, _taskQueue);
}
@end

View File

@@ -108,7 +108,6 @@
OCMStub([codec encodeMethodCall:[OCMArg any]]).andReturn(encodedMethodCall);
FlutterMethodCallHandler handler =
^(FlutterMethodCall* _Nonnull call, FlutterResult _Nonnull result) {
NSLog(@"hey");
};
[channel setMethodCallHandler:handler];
OCMVerify([binaryMessenger setMessageHandlerOnChannel:channelName
@@ -175,7 +174,6 @@
binaryMessenger:binaryMessenger
codec:codec];
FlutterMessageHandler handler = ^(id _Nullable message, FlutterReply callback) {
NSLog(@"hey");
};
OCMStub([binaryMessenger setMessageHandlerOnChannel:channelName
binaryMessageHandler:[OCMArg any]])
@@ -211,4 +209,78 @@
OCMVerify([binaryMessenger cleanUpConnection:connection]);
}
- (void)testBasicMessageChannelTaskQueue {
NSString* channelName = @"foo";
FlutterBinaryMessengerConnection connection = 123;
id binaryMessenger = OCMProtocolMock(@protocol(FlutterBinaryMessenger));
id codec = OCMProtocolMock(@protocol(FlutterMethodCodec));
id taskQueue = OCMClassMock([NSObject class]);
FlutterBasicMessageChannel* channel =
[[FlutterBasicMessageChannel alloc] initWithName:channelName
binaryMessenger:binaryMessenger
codec:codec
taskQueue:taskQueue];
FlutterMessageHandler handler = ^(id _Nullable message, FlutterReply callback) {
};
OCMStub([binaryMessenger setMessageHandlerOnChannel:channelName
binaryMessageHandler:[OCMArg any]
taskQueue:taskQueue])
.andReturn(connection);
[channel setMessageHandler:handler];
OCMVerify([binaryMessenger setMessageHandlerOnChannel:channelName
binaryMessageHandler:[OCMArg isNotNil]
taskQueue:taskQueue]);
[channel setMessageHandler:nil];
OCMVerify([binaryMessenger cleanUpConnection:connection]);
}
- (void)testMethodChannelTaskQueue {
NSString* channelName = @"foo";
FlutterBinaryMessengerConnection connection = 123;
id binaryMessenger = OCMProtocolMock(@protocol(FlutterBinaryMessenger));
id codec = OCMProtocolMock(@protocol(FlutterMethodCodec));
id taskQueue = OCMClassMock([NSObject class]);
FlutterMethodChannel* channel = [[FlutterMethodChannel alloc] initWithName:channelName
binaryMessenger:binaryMessenger
codec:codec
taskQueue:taskQueue];
XCTAssertNotNil(channel);
FlutterMethodCallHandler handler = ^(FlutterMethodCall* call, FlutterResult result) {
};
OCMStub([binaryMessenger setMessageHandlerOnChannel:channelName
binaryMessageHandler:[OCMArg any]
taskQueue:taskQueue])
.andReturn(connection);
[channel setMethodCallHandler:handler];
OCMVerify([binaryMessenger setMessageHandlerOnChannel:channelName
binaryMessageHandler:[OCMArg isNotNil]
taskQueue:taskQueue]);
[channel setMethodCallHandler:nil];
OCMVerify([binaryMessenger cleanUpConnection:connection]);
}
- (void)testEventChannelTaskQueue {
NSString* channelName = @"foo";
FlutterBinaryMessengerConnection connection = 123;
id binaryMessenger = OCMProtocolMock(@protocol(FlutterBinaryMessenger));
id codec = OCMProtocolMock(@protocol(FlutterMethodCodec));
id taskQueue = OCMClassMock([NSObject class]);
id handler = OCMProtocolMock(@protocol(FlutterStreamHandler));
FlutterEventChannel* channel = [[FlutterEventChannel alloc] initWithName:channelName
binaryMessenger:binaryMessenger
codec:codec
taskQueue:taskQueue];
XCTAssertNotNil(channel);
OCMStub([binaryMessenger setMessageHandlerOnChannel:channelName
binaryMessageHandler:[OCMArg any]
taskQueue:taskQueue])
.andReturn(connection);
[channel setStreamHandler:handler];
OCMVerify([binaryMessenger setMessageHandlerOnChannel:channelName
binaryMessageHandler:[OCMArg isNotNil]
taskQueue:taskQueue]);
[channel setStreamHandler:nil];
OCMVerify([binaryMessenger cleanUpConnection:connection]);
}
@end

View File

@@ -100,8 +100,6 @@ source_set("flutter_framework_source") {
"framework/Source/connection_collection.mm",
"framework/Source/platform_message_response_darwin.h",
"framework/Source/platform_message_response_darwin.mm",
"framework/Source/platform_message_router.h",
"framework/Source/platform_message_router.mm",
"framework/Source/profiler_metrics_ios.h",
"framework/Source/profiler_metrics_ios.mm",
"framework/Source/vsync_waiter_ios.h",
@@ -126,6 +124,8 @@ source_set("flutter_framework_source") {
"ios_surface_software.mm",
"ios_switchable_gl_context.h",
"ios_switchable_gl_context.mm",
"platform_message_handler_ios.h",
"platform_message_handler_ios.mm",
"platform_view_ios.h",
"platform_view_ios.mm",
"rendering_api_selection.h",
@@ -205,9 +205,12 @@ source_set("ios_test_flutter_mrc") {
"framework/Source/FlutterPlatformViewsTest.mm",
"framework/Source/FlutterViewTest.mm",
"framework/Source/accessibility_bridge_test.mm",
"platform_message_handler_ios_test.mm",
]
deps = [
":flutter_framework_source",
"//flutter/common:common",
"//flutter/shell/common:common",
"//flutter/shell/platform/darwin/common:framework_shared",
"//flutter/shell/platform/embedder:embedder_as_internal_library",
"//flutter/third_party/tonic",

View File

@@ -35,6 +35,14 @@
}
}
- (NSObject<FlutterTaskQueue>*)makeBackgroundTaskQueue {
if (self.parent) {
return [self.parent makeBackgroundTaskQueue];
} else {
return nil;
};
}
- (FlutterBinaryMessengerConnection)setMessageHandlerOnChannel:(NSString*)channel
binaryMessageHandler:
(FlutterBinaryMessageHandler)handler {
@@ -46,6 +54,20 @@
}
}
- (FlutterBinaryMessengerConnection)setMessageHandlerOnChannel:(NSString*)channel
binaryMessageHandler:(FlutterBinaryMessageHandler)handler
taskQueue:
(NSObject<FlutterTaskQueue>*)taskQueue {
if (self.parent) {
return [self.parent setMessageHandlerOnChannel:channel
binaryMessageHandler:handler
taskQueue:taskQueue];
} else {
FML_LOG(WARNING) << "Communicating on a dead channel.";
return -1;
}
}
- (void)cleanUpConnection:(FlutterBinaryMessengerConnection)connection {
if (self.parent) {
return [self.parent cleanUpConnection:connection];

View File

@@ -11,6 +11,9 @@
FLUTTER_ASSERT_ARC
@protocol FlutterTaskQueue <NSObject>
@end
@interface FlutterBinaryMessengerRelayTest : XCTestCase
@end
@@ -52,4 +55,26 @@ FLUTTER_ASSERT_ARC
[relay sendOnChannel:channel message:message binaryReply:nil];
}
- (void)testSetMessageHandlerWithTaskQueue {
id messenger = OCMProtocolMock(@protocol(FlutterBinaryMessenger));
FlutterBinaryMessengerRelay* relay =
[[FlutterBinaryMessengerRelay alloc] initWithParent:messenger];
NSString* channel = @"foobar";
NSObject<FlutterTaskQueue>* taskQueue = OCMProtocolMock(@protocol(FlutterTaskQueue));
FlutterBinaryMessageHandler handler = ^(NSData* _Nullable, FlutterBinaryReply _Nonnull) {
};
[relay setMessageHandlerOnChannel:channel binaryMessageHandler:handler taskQueue:taskQueue];
OCMVerify([messenger setMessageHandlerOnChannel:channel
binaryMessageHandler:handler
taskQueue:taskQueue]);
}
- (void)testMakeBackgroundTaskQueue {
id messenger = OCMProtocolMock(@protocol(FlutterBinaryMessenger));
FlutterBinaryMessengerRelay* relay =
[[FlutterBinaryMessengerRelay alloc] initWithParent:messenger];
[relay makeBackgroundTaskQueue];
OCMVerify([messenger makeBackgroundTaskQueue]);
}
@end

View File

@@ -952,12 +952,24 @@ static void SetEntryPoint(flutter::Settings* settings, NSString* entrypoint, NSS
_shell->GetPlatformView()->DispatchPlatformMessage(std::move(platformMessage));
}
- (NSObject<FlutterTaskQueue>*)makeBackgroundTaskQueue {
return flutter::PlatformMessageHandlerIos::MakeBackgroundTaskQueue();
}
- (FlutterBinaryMessengerConnection)setMessageHandlerOnChannel:(NSString*)channel
binaryMessageHandler:
(FlutterBinaryMessageHandler)handler {
return [self setMessageHandlerOnChannel:channel binaryMessageHandler:handler taskQueue:nil];
}
- (FlutterBinaryMessengerConnection)
setMessageHandlerOnChannel:(NSString*)channel
binaryMessageHandler:(FlutterBinaryMessageHandler)handler
taskQueue:(NSObject<FlutterTaskQueue>* _Nullable)taskQueue {
NSParameterAssert(channel);
if (_shell && _shell->IsSetup()) {
self.iosPlatformView->GetPlatformMessageRouter().SetMessageHandler(channel.UTF8String, handler);
self.iosPlatformView->GetPlatformMessageHandlerIos()->SetMessageHandler(channel.UTF8String,
handler, taskQueue);
return _connections->AquireConnection(channel.UTF8String);
} else {
NSAssert(!handler, @"Setting a message handler before the FlutterEngine has been run.");
@@ -970,7 +982,8 @@ static void SetEntryPoint(flutter::Settings* settings, NSString* entrypoint, NSS
if (_shell && _shell->IsSetup()) {
std::string channel = _connections->CleanupConnection(connection);
if (!channel.empty()) {
self.iosPlatformView->GetPlatformMessageRouter().SetMessageHandler(channel.c_str(), nil);
self.iosPlatformView->GetPlatformMessageHandlerIos()->SetMessageHandler(channel.c_str(), nil,
nil);
}
}
}

View File

@@ -1604,12 +1604,24 @@ static flutter::PointerData::DeviceKind DeviceKindFromTouchType(UITouch* touch)
[_engine.get().binaryMessenger sendOnChannel:channel message:message binaryReply:callback];
}
- (NSObject<FlutterTaskQueue>*)makeBackgroundTaskQueue {
return [_engine.get().binaryMessenger makeBackgroundTaskQueue];
}
- (FlutterBinaryMessengerConnection)setMessageHandlerOnChannel:(NSString*)channel
binaryMessageHandler:
(FlutterBinaryMessageHandler)handler {
return [self setMessageHandlerOnChannel:channel binaryMessageHandler:handler taskQueue:nil];
}
- (FlutterBinaryMessengerConnection)
setMessageHandlerOnChannel:(NSString*)channel
binaryMessageHandler:(FlutterBinaryMessageHandler _Nullable)handler
taskQueue:(NSObject<FlutterTaskQueue>* _Nullable)taskQueue {
NSAssert(channel, @"The channel must not be null");
return [_engine.get().binaryMessenger setMessageHandlerOnChannel:channel
binaryMessageHandler:handler];
binaryMessageHandler:handler
taskQueue:taskQueue];
}
- (void)cleanUpConnection:(FlutterBinaryMessengerConnection)connection {

View File

@@ -1,37 +0,0 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_PLATFORM_MESSAGE_ROUTER_H_
#define SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_PLATFORM_MESSAGE_ROUTER_H_
#include <unordered_map>
#include "flutter/fml/memory/weak_ptr.h"
#include "flutter/fml/platform/darwin/scoped_block.h"
#include "flutter/lib/ui/window/platform_message.h"
#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterBinaryMessenger.h"
namespace flutter {
class PlatformMessageRouter {
public:
PlatformMessageRouter();
~PlatformMessageRouter();
void HandlePlatformMessage(
std::unique_ptr<flutter::PlatformMessage> message) const;
void SetMessageHandler(const std::string& channel,
FlutterBinaryMessageHandler handler);
private:
std::unordered_map<std::string, fml::ScopedBlock<FlutterBinaryMessageHandler>>
message_handlers_;
FML_DISALLOW_COPY_AND_ASSIGN(PlatformMessageRouter);
};
} // namespace flutter
#endif // SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_ACCESSIBILITY_BRIDGE_H_

View File

@@ -1,52 +0,0 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "flutter/shell/platform/darwin/ios/framework/Source/platform_message_router.h"
#include <vector>
#import "flutter/shell/platform/darwin/common/buffer_conversions.h"
namespace flutter {
PlatformMessageRouter::PlatformMessageRouter() = default;
PlatformMessageRouter::~PlatformMessageRouter() = default;
void PlatformMessageRouter::HandlePlatformMessage(
std::unique_ptr<flutter::PlatformMessage> message) const {
fml::RefPtr<flutter::PlatformMessageResponse> completer = message->response();
auto it = message_handlers_.find(message->channel());
if (it != message_handlers_.end()) {
FlutterBinaryMessageHandler handler = it->second;
NSData* data = nil;
if (message->hasData()) {
data = ConvertMappingToNSData(message->releaseData());
}
handler(data, ^(NSData* reply) {
if (completer) {
if (reply) {
completer->Complete(ConvertNSDataToMappingPtr(reply));
} else {
completer->CompleteEmpty();
}
}
});
} else {
if (completer) {
completer->CompleteEmpty();
}
}
}
void PlatformMessageRouter::SetMessageHandler(const std::string& channel,
FlutterBinaryMessageHandler handler) {
message_handlers_.erase(channel);
if (handler) {
message_handlers_[channel] =
fml::ScopedBlock<FlutterBinaryMessageHandler>{handler, fml::OwnershipPolicy::Retain};
}
}
} // namespace flutter

View File

@@ -0,0 +1,51 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef FLUTTER_SHELL_PLATFORM_DARWIN_IOS_PLATFORM_MESSAGE_HANDLER_IOS_H_
#define FLUTTER_SHELL_PLATFORM_DARWIN_IOS_PLATFORM_MESSAGE_HANDLER_IOS_H_
#include <unordered_map>
#include "flutter/common/task_runners.h"
#include "flutter/fml/platform/darwin/scoped_block.h"
#include "flutter/fml/platform/darwin/scoped_nsobject.h"
#include "flutter/shell/common/platform_message_handler.h"
#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterBinaryMessenger.h"
@protocol FlutterTaskQueue;
namespace flutter {
class PlatformMessageHandlerIos : public PlatformMessageHandler {
public:
static NSObject<FlutterTaskQueue>* MakeBackgroundTaskQueue();
PlatformMessageHandlerIos(TaskRunners task_runners);
void HandlePlatformMessage(std::unique_ptr<PlatformMessage> message) override;
void InvokePlatformMessageResponseCallback(int response_id,
std::unique_ptr<fml::Mapping> mapping) override;
void InvokePlatformMessageEmptyResponseCallback(int response_id) override;
void SetMessageHandler(const std::string& channel,
FlutterBinaryMessageHandler handler,
NSObject<FlutterTaskQueue>* task_queue);
struct HandlerInfo {
fml::scoped_nsprotocol<NSObject<FlutterTaskQueue>*> task_queue;
fml::ScopedBlock<FlutterBinaryMessageHandler> handler;
};
private:
std::unordered_map<std::string, HandlerInfo> message_handlers_;
TaskRunners task_runners_;
std::mutex message_handlers_mutex_;
FML_DISALLOW_COPY_AND_ASSIGN(PlatformMessageHandlerIos);
};
} // namespace flutter
#endif

View File

@@ -0,0 +1,118 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "flutter/shell/platform/darwin/ios/platform_message_handler_ios.h"
#import "flutter/shell/platform/darwin/common/buffer_conversions.h"
#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterBinaryMessenger.h"
@protocol FlutterTaskQueue
- (void)dispatch:(dispatch_block_t)block;
@end
@interface FLTSerialTaskQueue : NSObject <FlutterTaskQueue>
@property(nonatomic, strong) dispatch_queue_t queue;
@end
@implementation FLTSerialTaskQueue
- (instancetype)init {
self = [super init];
if (self) {
_queue = dispatch_queue_create("FLTSerialTaskQueue", DISPATCH_QUEUE_SERIAL);
}
return self;
}
- (void)dealloc {
dispatch_release(_queue);
[super dealloc];
}
- (void)dispatch:(dispatch_block_t)block {
dispatch_async(self.queue, block);
}
@end
namespace flutter {
NSObject<FlutterTaskQueue>* PlatformMessageHandlerIos::MakeBackgroundTaskQueue() {
return [[[FLTSerialTaskQueue alloc] init] autorelease];
}
PlatformMessageHandlerIos::PlatformMessageHandlerIos(TaskRunners task_runners)
: task_runners_(task_runners) {}
void PlatformMessageHandlerIos::HandlePlatformMessage(std::unique_ptr<PlatformMessage> message) {
FML_CHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread());
fml::RefPtr<flutter::PlatformMessageResponse> completer = message->response();
HandlerInfo handler_info;
{
std::lock_guard lock(message_handlers_mutex_);
auto it = message_handlers_.find(message->channel());
if (it != message_handlers_.end()) {
handler_info = it->second;
}
}
if (handler_info.handler) {
FlutterBinaryMessageHandler handler = handler_info.handler;
NSData* data = nil;
if (message->hasData()) {
data = ConvertMappingToNSData(message->releaseData());
}
dispatch_block_t run_handler = ^{
handler(data, ^(NSData* reply) {
// Called from any thread.
if (completer) {
if (reply) {
completer->Complete(ConvertNSDataToMappingPtr(reply));
} else {
completer->CompleteEmpty();
}
}
});
};
if (handler_info.task_queue.get()) {
[handler_info.task_queue.get() dispatch:run_handler];
} else {
dispatch_async(dispatch_get_main_queue(), run_handler);
}
} else {
if (completer) {
completer->CompleteEmpty();
}
}
}
void PlatformMessageHandlerIos::InvokePlatformMessageResponseCallback(
int response_id,
std::unique_ptr<fml::Mapping> mapping) {
// Called from any thread.
// TODO(gaaclarke): This vestigal from the Android implementation, find a way
// to migrate this to PlatformMessageHandlerAndroid.
}
void PlatformMessageHandlerIos::InvokePlatformMessageEmptyResponseCallback(int response_id) {
// Called from any thread.
// TODO(gaaclarke): This vestigal from the Android implementation, find a way
// to migrate this to PlatformMessageHandlerAndroid.
}
void PlatformMessageHandlerIos::SetMessageHandler(const std::string& channel,
FlutterBinaryMessageHandler handler,
NSObject<FlutterTaskQueue>* task_queue) {
FML_CHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
/// TODO(gaaclarke): This should be migrated to a lockfree datastructure.
std::lock_guard lock(message_handlers_mutex_);
message_handlers_.erase(channel);
if (handler) {
message_handlers_[channel] = {
.task_queue = fml::scoped_nsprotocol([task_queue retain]),
.handler =
fml::ScopedBlock<FlutterBinaryMessageHandler>{handler, fml::OwnershipPolicy::Retain},
};
}
}
} // namespace flutter

View File

@@ -0,0 +1,132 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import <XCTest/XCTest.h>
#import "flutter/shell/platform/darwin/ios/platform_message_handler_ios.h"
#import "flutter/common/task_runners.h"
#import "flutter/fml/message_loop.h"
#import "flutter/fml/thread.h"
#import "flutter/shell/common/thread_host.h"
#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h"
FLUTTER_ASSERT_NOT_ARC
namespace {
using namespace flutter;
fml::RefPtr<fml::TaskRunner> CreateNewThread(std::string name) {
auto thread = std::make_unique<fml::Thread>(name);
auto runner = thread->GetTaskRunner();
return runner;
}
fml::RefPtr<fml::TaskRunner> GetCurrentTaskRunner() {
fml::MessageLoop::EnsureInitializedForCurrentThread();
return fml::MessageLoop::GetCurrent().GetTaskRunner();
}
class MockPlatformMessageResponse : public PlatformMessageResponse {
public:
static fml::RefPtr<MockPlatformMessageResponse> Create() {
return fml::AdoptRef(new MockPlatformMessageResponse());
}
void Complete(std::unique_ptr<fml::Mapping> data) override { is_complete_ = true; }
void CompleteEmpty() override { is_complete_ = true; }
};
} // namespace
@interface PlatformMessageHandlerIosTest : XCTestCase
@end
@implementation PlatformMessageHandlerIosTest
- (void)testCreate {
flutter::TaskRunners task_runners("test", GetCurrentTaskRunner(), CreateNewThread("raster"),
CreateNewThread("ui"), CreateNewThread("io"));
auto handler = std::make_unique<PlatformMessageHandlerIos>(task_runners);
XCTAssertTrue(handler);
}
- (void)testSetAndCallHandler {
ThreadHost thread_host("io.flutter.test." + std::string(self.name.UTF8String),
ThreadHost::Type::RASTER | ThreadHost::Type::IO | ThreadHost::Type::UI);
TaskRunners task_runners(
"test", GetCurrentTaskRunner(), thread_host.raster_thread->GetTaskRunner(),
thread_host.ui_thread->GetTaskRunner(), thread_host.io_thread->GetTaskRunner());
auto handler = std::make_unique<PlatformMessageHandlerIos>(task_runners);
std::string channel = "foo";
XCTestExpectation* didCallReply = [self expectationWithDescription:@"didCallReply"];
handler->SetMessageHandler(
channel,
^(NSData* _Nullable data, FlutterBinaryReply _Nonnull reply) {
reply(nil);
[didCallReply fulfill];
},
nil);
auto response = MockPlatformMessageResponse::Create();
task_runners.GetUITaskRunner()->PostTask([channel, response, &handler] {
auto platform_message = std::make_unique<flutter::PlatformMessage>(channel, response);
handler->HandlePlatformMessage(std::move(platform_message));
});
[self waitForExpectationsWithTimeout:1.0 handler:nil];
XCTAssertTrue(response->is_complete());
}
- (void)testSetClearAndCallHandler {
ThreadHost thread_host("io.flutter.test." + std::string(self.name.UTF8String),
ThreadHost::Type::RASTER | ThreadHost::Type::IO | ThreadHost::Type::UI);
TaskRunners task_runners(
"test", GetCurrentTaskRunner(), thread_host.raster_thread->GetTaskRunner(),
thread_host.ui_thread->GetTaskRunner(), thread_host.io_thread->GetTaskRunner());
auto handler = std::make_unique<PlatformMessageHandlerIos>(task_runners);
std::string channel = "foo";
XCTestExpectation* didCallMessage = [self expectationWithDescription:@"didCallMessage"];
handler->SetMessageHandler(
channel,
^(NSData* _Nullable data, FlutterBinaryReply _Nonnull reply) {
XCTFail(@"This shouldn't be called");
reply(nil);
},
nil);
handler->SetMessageHandler(channel, nil, nil);
auto response = MockPlatformMessageResponse::Create();
task_runners.GetUITaskRunner()->PostTask([channel, response, &handler, &didCallMessage] {
auto platform_message = std::make_unique<flutter::PlatformMessage>(channel, response);
handler->HandlePlatformMessage(std::move(platform_message));
[didCallMessage fulfill];
});
[self waitForExpectationsWithTimeout:1.0 handler:nil];
XCTAssertTrue(response->is_complete());
}
- (void)testSetAndCallHandlerTaskQueue {
ThreadHost thread_host("io.flutter.test." + std::string(self.name.UTF8String),
ThreadHost::Type::RASTER | ThreadHost::Type::IO | ThreadHost::Type::UI);
TaskRunners task_runners(
"test", GetCurrentTaskRunner(), thread_host.raster_thread->GetTaskRunner(),
thread_host.ui_thread->GetTaskRunner(), thread_host.io_thread->GetTaskRunner());
auto handler = std::make_unique<PlatformMessageHandlerIos>(task_runners);
std::string channel = "foo";
XCTestExpectation* didCallReply = [self expectationWithDescription:@"didCallReply"];
NSObject<FlutterTaskQueue>* taskQueue = PlatformMessageHandlerIos::MakeBackgroundTaskQueue();
handler->SetMessageHandler(
channel,
^(NSData* _Nullable data, FlutterBinaryReply _Nonnull reply) {
XCTAssertFalse([NSThread isMainThread]);
reply(nil);
[didCallReply fulfill];
},
taskQueue);
auto response = MockPlatformMessageResponse::Create();
task_runners.GetUITaskRunner()->PostTask([channel, response, &handler] {
auto platform_message = std::make_unique<flutter::PlatformMessage>(channel, response);
handler->HandlePlatformMessage(std::move(platform_message));
});
[self waitForExpectationsWithTimeout:1.0 handler:nil];
XCTAssertTrue(response->is_complete());
}
@end

View File

@@ -16,10 +16,10 @@
#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterView.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/accessibility_bridge.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/platform_message_router.h"
#import "flutter/shell/platform/darwin/ios/ios_context.h"
#import "flutter/shell/platform/darwin/ios/ios_external_view_embedder.h"
#import "flutter/shell/platform/darwin/ios/ios_surface.h"
#import "flutter/shell/platform/darwin/ios/platform_message_handler_ios.h"
#import "flutter/shell/platform/darwin/ios/rendering_api_selection.h"
@class FlutterViewController;
@@ -53,12 +53,6 @@ class PlatformViewIOS final : public PlatformView {
~PlatformViewIOS() override;
/**
* The `PlatformMessageRouter` is the iOS bridge connecting the shell's
* platform agnostic `PlatformMessage` to iOS's channel message handler.
*/
PlatformMessageRouter& GetPlatformMessageRouter();
/**
* Returns the `FlutterViewController` currently attached to the `FlutterEngine` owning
* this PlatformViewIOS.
@@ -96,6 +90,14 @@ class PlatformViewIOS final : public PlatformView {
/** Accessor for the `IOSContext` associated with the platform view. */
const std::shared_ptr<IOSContext>& GetIosContext() { return ios_context_; }
std::shared_ptr<PlatformMessageHandlerIos> GetPlatformMessageHandlerIos() const {
return platform_message_handler_;
}
std::shared_ptr<PlatformMessageHandler> GetPlatformMessageHandler() const override {
return platform_message_handler_;
}
private:
/// Smart pointer for use with objective-c observers.
/// This guarantees we remove the observer.
@@ -136,12 +138,12 @@ class PlatformViewIOS final : public PlatformView {
std::unique_ptr<IOSSurface> ios_surface_;
std::shared_ptr<IOSContext> ios_context_;
const std::shared_ptr<FlutterPlatformViewsController>& platform_views_controller_;
PlatformMessageRouter platform_message_router_;
AccessibilityBridgePtr accessibility_bridge_;
fml::scoped_nsprotocol<FlutterTextInputPlugin*> text_input_plugin_;
fml::closure firstFrameCallback_;
ScopedObserver dealloc_view_controller_observer_;
std::vector<std::string> platform_resolved_locale_;
std::shared_ptr<PlatformMessageHandlerIos> platform_message_handler_;
// |PlatformView|
void HandlePlatformMessage(std::unique_ptr<flutter::PlatformMessage> message) override;

View File

@@ -53,7 +53,8 @@ PlatformViewIOS::PlatformViewIOS(
: PlatformView(delegate, std::move(task_runners)),
ios_context_(context),
platform_views_controller_(platform_views_controller),
accessibility_bridge_([this](bool enabled) { PlatformView::SetSemanticsEnabled(enabled); }) {}
accessibility_bridge_([this](bool enabled) { PlatformView::SetSemanticsEnabled(enabled); }),
platform_message_handler_(new PlatformMessageHandlerIos(task_runners)) {}
PlatformViewIOS::PlatformViewIOS(
PlatformView::Delegate& delegate,
@@ -67,13 +68,9 @@ PlatformViewIOS::PlatformViewIOS(
PlatformViewIOS::~PlatformViewIOS() = default;
PlatformMessageRouter& PlatformViewIOS::GetPlatformMessageRouter() {
return platform_message_router_;
}
// |PlatformView|
void PlatformViewIOS::HandlePlatformMessage(std::unique_ptr<flutter::PlatformMessage> message) {
platform_message_router_.HandlePlatformMessage(std::move(message));
platform_message_handler_->HandlePlatformMessage(std::move(message));
}
fml::WeakPtr<FlutterViewController> PlatformViewIOS::GetOwnerViewController() const {