Flutter 与 Native(iOS) 通信原理
Flutter 与 Native 通信原理
Flutter 是一个跨平台开发框架,它使用了一种全新的方式,自己重写了一个平台无关的渲染引擎,它只提供画布,所有的 UI 组件、渲染逻辑都是在这个引擎上处理的。但某些平台独有的功能特性,仍由平台自己处理,Flutter 提供了 Platform Channel 机制,让消息能够在 native 与 Flutter 之间进行传递。
接下来我们将深入 Flutter engine 内部,看看 Flutter 是如何调用 native 模块,native 如何调用 Flutter,数据是如何在两端传递。
Platform Channel
每个 Channel 都有一个独一无二的名字,Channel 之间通过 name 区分彼此。
Channel 使用 codec 消息编解码器,支持从基础数据到二进制格式数据的转换、解析。
Channel 有三种类型,分别是:
BasicMessageChannel:用于传递基本数据
MethodChannel: 用于传递方法调用,Flutter 侧调用 native 侧的功能,并获取处理结果。
EventChannel:用于向 Flutter 侧传递事件,native 侧主动发消息给 Flutter。
这三种类型比较相似,因为它们都是传递数据,实现方式也比较类似。
Channel 注册
用官方的获取电量的 Demo 来看看 Flutter 如何与 native通信。我们从调用处开始进入 Flutter engine,一步步跟踪代码运行过程。
在 iOS 项目中,注册获取电量的 channel
- (BOOL)application:(UIApplication*)application
didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
[GeneratedPluginRegistrant registerWithRegistry:self];
FlutterViewController* controller =
(FlutterViewController*)self.window.rootViewController;
FlutterMethodChannel* batteryChannel = [FlutterMethodChannel
methodChannelWithName:@"samples.flutter.io/battery"
binaryMessenger:controller];
__weak typeof(self) weakSelf = self;
[batteryChannel setMethodCallHandler:^(FlutterMethodCall* call,
FlutterResult result) {
if ([@"getBatteryLevel" isEqualToString:call.method]) {
int batteryLevel = [weakSelf getBatteryLevel];
if (batteryLevel == -1) {
result([FlutterError errorWithCode:@"UNAVAILABLE"
message:@"Battery info unavailable"
details:nil]);
} else {
result(@(batteryLevel));
}
} else {
result(FlutterMethodNotImplemented);
}
}];
FlutterEventChannel* chargingChannel = [FlutterEventChannel
eventChannelWithName:@"samples.flutter.io/charging"
binaryMessenger:controller];
[chargingChannel setStreamHandler:self];
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
代码简化一下
FlutterMethodChannel* batteryChannel = [FlutterMethodChannel
methodChannelWithName:@"samples.flutter.io/battery"
binaryMessenger:controller];
[batteryChannel setMethodCallHandler:^(FlutterMethodCall* call,
FlutterResult result) {
if ([@"getBatteryLevel" isEqualToString:call.method]) {
int batteryLevel = [weakSelf getBatteryLevel];
if (batteryLevel == -1) {
result([FlutterError errorWithCode:@"UNAVAILABLE"
message:@"Battery info unavailable"
details:nil]);
} else {
result(@(batteryLevel));
}
......
}];
可以看到,首先初始化一个桥接,传入桥接名和处理消息发送接收的类,桥接名为"samples.flutter.io/battery",处理消息的类为FlutterViewController。
通过setMethodCallHandler
方法,在 native 项目中注册该桥接的回调 handler,其中参数 result 表示向 Flutter 回传的结果。
- (void)setMethodCallHandler:(FlutterMethodCallHandler)handler {
if (!handler) {
[_messenger setMessageHandlerOnChannel:_name binaryMessageHandler:nil];
return;
}
FlutterBinaryMessageHandler messageHandler = ^(NSData* message, FlutterBinaryReply callback) {
FlutterMethodCall* call = [_codec decodeMethodCall:message];
handler(call, ^(id result) {
if (result == FlutterMethodNotImplemented)
callback(nil);
else if ([result isKindOfClass:[FlutterError class]])
callback([_codec encodeErrorEnvelope:(FlutterError*)result]);
else
callback([_codec encodeSuccessEnvelope:result]);
});
};
[_messenger setMessageHandlerOnChannel:_name binaryMessageHandler:messageHandler];
}
回顾上面的代码,我们知道 _messenger
就是 FlutterViewController
,此处又包装了一个回调 messageHandler,用于解码二进制消息 message,并向 Flutter 侧回传执行结果 reply。
- (void)setMessageHandlerOnChannel:(NSString*)channel
binaryMessageHandler:(FlutterBinaryMessageHandler)handler {
NSAssert(channel, @"The channel must not be null");
[_engine.get() setMessageHandlerOnChannel:channel binaryMessageHandler:handler];
}
FlutterViewController 不做处理,将注册事件转发给 FlutterEngine。
- (void)setMessageHandlerOnChannel:(NSString*)channel
binaryMessageHandler:(FlutterBinaryMessageHandler)handler {
NSAssert(channel, @"The channel must not be null");
FML_DCHECK(_shell && _shell->IsSetup());
self.iosPlatformView->GetPlatformMessageRouter().SetMessageHandler(channel.UTF8String, handler);
}
FlutterEngine 又将桥接事件转发给 PlatformViewIOS 的 PlatformMessageRouter
// PlatformMessageRouter
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};
}
}
PlatformMessageRouter 的属性 message_handlers_ 是个哈希表,key 是桥接名,value 放 handle。原生注册桥接方法,其实就是维护一个 map 对象。
至此,注册原生方法完成了,整个流程如下
image.pngFlutter 调用 native 功能
先来看下 dart 侧如何调用获取电量的桥接方法
static const MethodChannel methodChannel =
MethodChannel('samples.flutter.io/battery');
final int result = await methodChannel.invokeMethod('getBatteryLevel');
跟踪进去
// platform_channel.dart
const MethodChannel(this.name, [this.codec = const StandardMethodCodec()]);
Future<dynamic> invokeMethod(String method, [dynamic arguments]) async {
assert(method != null);
final dynamic result = await BinaryMessages.send(
name,
codec.encodeMethodCall(MethodCall(method, arguments)),
);
if (result == null)
throw MissingPluginException('No implementation found for method $method on channel $name');
return codec.decodeEnvelope(result);
}
invokeMethod 方法将方法名和参数转化为二进制数据,并通过 BinaryMessages send 方法发送出去。
// platform_messages.dart
static Future<ByteData> send(String channel, ByteData message) {
final _MessageHandler handler = _mockHandlers[channel];
if (handler != null)
return handler(message);
return _sendPlatformMessage(channel, message);
}
static Future<ByteData> _sendPlatformMessage(String channel, ByteData message) {
final Completer<ByteData> completer = Completer<ByteData>();
ui.window.sendPlatformMessage(channel, message, (ByteData reply) {
try {
completer.complete(reply);
} catch (exception, stack) {
FlutterError.reportError(FlutterErrorDetails(
exception: exception,
stack: stack,
library: 'services library',
context: 'during a platform message response callback',
));
}
});
return completer.future;
}
_mockHandlers 是一个map,存放需要 mock 的桥接,如果想拦截 mock 某个桥接,会在这里处理。
不是 mock 的桥接,则转发到 window.dart sendPlatformMessage方法。
// window.dart
void sendPlatformMessage(String name,
ByteData data,
PlatformMessageResponseCallback callback) {
final String error =
_sendPlatformMessage(name, _zonedPlatformMessageResponseCallback(callback), data);
if (error != null)
throw new Exception(error);
}
String _sendPlatformMessage(String name,
PlatformMessageResponseCallback callback,
ByteData data) native 'Window_sendPlatformMessage';
_sendPlatformMessage 将调用 native 的方法 Window_sendPlatformMessage。
dart 侧的代码跟踪结束了,接下来又到了 native 侧。
void Window::RegisterNatives(tonic::DartLibraryNatives* natives) {
natives->Register({
{"Window_defaultRouteName", DefaultRouteName, 1, true},
{"Window_scheduleFrame", ScheduleFrame, 1, true},
{"Window_sendPlatformMessage", _SendPlatformMessage, 4, true},
{"Window_respondToPlatformMessage", _RespondToPlatformMessage, 3, true},
{"Window_render", Render, 2, true},
{"Window_updateSemantics", UpdateSemantics, 2, true},
{"Window_setIsolateDebugName", SetIsolateDebugName, 2, true},
});
}
注册 native 方法,供 dart 端调用,其中 Window_sendPlatformMessage 被 dart 调用,对应的 _SendPlatformMessage 方法。而 _SendPlatformMessage 又调用了SendPlatformMessage
// WindowClient
Dart_Handle SendPlatformMessage(Dart_Handle window,
const std::string& name,
Dart_Handle callback,
const tonic::DartByteData& data) {
UIDartState* dart_state = UIDartState::Current();
if (!dart_state->window()) {
// Must release the TypedData buffer before allocating other Dart objects.
data.Release();
return ToDart("Platform messages can only be sent from the main isolate");
}
fml::RefPtr<PlatformMessageResponse> response;
if (!Dart_IsNull(callback)) {
response = fml::MakeRefCounted<PlatformMessageResponseDart>(
tonic::DartPersistentValue(dart_state, callback),
dart_state->GetTaskRunners().GetUITaskRunner());
}
if (Dart_IsNull(data.dart_handle())) {
dart_state->window()->client()->HandlePlatformMessage(
fml::MakeRefCounted<PlatformMessage>(name, response));
} else {
const uint8_t* buffer = static_cast<const uint8_t*>(data.data());
dart_state->window()->client()->HandlePlatformMessage(
fml::MakeRefCounted<PlatformMessage>(
name, std::vector<uint8_t>(buffer, buffer + data.length_in_bytes()),
response));
}
return Dart_Null();
}
该方法最终会调用WindowClient的HandlePlatformMessage方法。
WindowClient是父类,由子类RuntimeController具体实现,RuntimeController又将该消息交给其代理RuntimeDelegate处理,而RuntimeDelegate的具体实现则是Engine类
// Engine.cc
void Engine::HandlePlatformMessage(
fml::RefPtr<blink::PlatformMessage> message) {
if (message->channel() == kAssetChannel) {
HandleAssetPlatformMessage(std::move(message));
} else {
delegate_.OnEngineHandlePlatformMessage(std::move(message));
}
}
首先检查是否为了获取资源,如果是,则走获取资源逻辑,否则走Engin的代理delegate逻辑,delegate的实现是Shell
// |shell::Engine::Delegate|
void Shell::OnEngineHandlePlatformMessage(
fml::RefPtr<blink::PlatformMessage> message) {
FML_DCHECK(is_setup_);
FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread());
task_runners_.GetPlatformTaskRunner()->PostTask(
[view = platform_view_->GetWeakPtr(), message = std::move(message)]() {
if (view) {
view->HandlePlatformMessage(std::move(message));
}
});
}
收到消息后向 PlatformTaskRunner 添加一个 task,task 内调用PlatformView(iOS中实现类为PlatfromViewIOS)的 HandlePlatformMessage 方法
// |shell::PlatformView|
void PlatformViewIOS::HandlePlatformMessage(fml::RefPtr<blink::PlatformMessage> message) {
platform_message_router_.HandlePlatformMessage(std::move(message));
}
把消息转发给PlatformMessageRouter
void PlatformMessageRouter::HandlePlatformMessage(
fml::RefPtr<blink::PlatformMessage> message) const {
fml::RefPtr<blink::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 = GetNSDataFromVector(message->data());
}
handler(data, ^(NSData* reply) {
if (completer) {
if (reply) {
completer->Complete(GetMappingFromNSData(reply));
} else {
completer->CompleteEmpty();
}
}
});
} else {
if (completer) {
completer->CompleteEmpty();
}
}
}
从message_handlers_中取出channelName对应的 handle 并执行
handle 完成后,将结果回调给 Flutter
流程如下
image.pngNative 调用 Flutter 功能
在 Flutter 侧,注册监听,处理接收从 native 侧发来的消息
Stream<dynamic> receiveBroadcastStream([dynamic arguments]) {
final MethodChannel methodChannel = MethodChannel(name, codec);
StreamController<dynamic> controller;
controller = StreamController<dynamic>.broadcast(onListen: () async {
BinaryMessages.setMessageHandler(name, (ByteData reply) async {
if (reply == null) {
controller.close();
} else {
try {
controller.add(codec.decodeEnvelope(reply));
} on PlatformException catch (e) {
controller.addError(e);
}
}
return null;
});
try {
await methodChannel.invokeMethod('listen', arguments);
} catch (exception, stack) {
FlutterError.reportError(FlutterErrorDetails(
exception: exception,
stack: stack,
library: 'services library',
context: 'while activating platform stream on channel $name',
));
}
}, onCancel: () async {
BinaryMessages.setMessageHandler(name, null);
try {
await methodChannel.invokeMethod('cancel', arguments);
} catch (exception, stack) {
FlutterError.reportError(FlutterErrorDetails(
exception: exception,
stack: stack,
library: 'services library',
context: 'while de-activating platform stream on channel $name',
));
}
});
return controller.stream;
}
BinaryMessages.setMessageHandler 向 dart 侧的全局map添加 key 为 name 的事件,当接收到从 native 传来的消息时,找到就执行对应 handle。
该方法向 native 侧发送了一个名为 listen 的消息,这部分即是上面分析的 Flutter 调用 native 功能,不再赘述。
在 native 侧,注册key 为 listen 的桥接事件,key、handle 同样放到 message_handlers_ 中,如下
- (void)setStreamHandler:(NSObject<FlutterStreamHandler>*)handler {
if (!handler) {
[_messenger setMessageHandlerOnChannel:_name binaryMessageHandler:nil];
return;
}
__block FlutterEventSink currentSink = nil;
FlutterBinaryMessageHandler messageHandler = ^(NSData* message, FlutterBinaryReply callback) {
FlutterMethodCall* call = [_codec decodeMethodCall:message];
if ([call.method isEqual:@"listen"]) {
if (currentSink) {
FlutterError* error = [handler onCancelWithArguments:nil];
if (error)
NSLog(@"Failed to cancel existing stream: %@. %@ (%@)", error.code, error.message,
error.details);
}
currentSink = ^(id event) {
if (event == FlutterEndOfEventStream)
[_messenger sendOnChannel:_name message:nil];
else if ([event isKindOfClass:[FlutterError class]])
[_messenger sendOnChannel:_name
message:[_codec encodeErrorEnvelope:(FlutterError*)event]];
else
[_messenger sendOnChannel:_name message:[_codec encodeSuccessEnvelope:event]];
};
FlutterError* error = [handler onListenWithArguments:call.arguments eventSink:currentSink];
if (error)
callback([_codec encodeErrorEnvelope:error]);
else
callback([_codec encodeSuccessEnvelope:nil]);
} else if ([call.method isEqual:@"cancel"]) {
if (!currentSink) {
callback(
[_codec encodeErrorEnvelope:[FlutterError errorWithCode:@"error"
message:@"No active stream to cancel"
details:nil]]);
return;
}
currentSink = nil;
FlutterError* error = [handler onCancelWithArguments:call.arguments];
if (error)
callback([_codec encodeErrorEnvelope:error]);
else
callback([_codec encodeSuccessEnvelope:nil]);
} else {
callback(nil);
}
};
[_messenger setMessageHandlerOnChannel:_name binaryMessageHandler:messageHandler];
}
当接收到从 dart 侧发来的 listen 事件,取出对应的 handle 并执行。
这个 handle 将 FlutterEventSink 传给 native 调用方。
FlutterEventSink 把参数编码后传给 FlutterViewController,FlutterViewController 依次转发给 FlutterEngine、PlatformViewIOS,最终转发到 RuntimeController DispatchPlatformMessage。向 dart 侧发出该消息。
流程如下
image.png参考资料:
Flutter
Flutter engine
深入理解Flutter Platform Channel
Flutter与Native通信 - PlatformChannel源码分析