Flutter 與 Native 通信原理
Flutter 是一個(gè)跨平臺(tái)開發(fā)框架仰禽,它使用了一種全新的方式纺蛆,自己重寫了一個(gè)平臺(tái)無關(guān)的渲染引擎勇边,它只提供畫布,所有的 UI 組件粒褒、渲染邏輯都是在這個(gè)引擎上處理的诚镰。但某些平臺(tái)獨(dú)有的功能特性,仍由平臺(tái)自己處理月杉,F(xiàn)lutter 提供了 Platform Channel 機(jī)制抠艾,讓消息能夠在 native 與 Flutter 之間進(jìn)行傳遞。
接下來我們將深入 Flutter engine 內(nèi)部检号,看看 Flutter 是如何調(diào)用 native 模塊,native 如何調(diào)用 Flutter翘盖,數(shù)據(jù)是如何在兩端傳遞。
Platform Channel
每個(gè) Channel 都有一個(gè)獨(dú)一無二的名字馍驯,Channel 之間通過 name 區(qū)分彼此玛痊。
Channel 使用 codec 消息編解碼器,支持從基礎(chǔ)數(shù)據(jù)到二進(jìn)制格式數(shù)據(jù)的轉(zhuǎn)換擂煞、解析。
Channel 有三種類型剑逃,分別是:
BasicMessageChannel:用于傳遞基本數(shù)據(jù)
MethodChannel: 用于傳遞方法調(diào)用官辽,F(xiàn)lutter 側(cè)調(diào)用 native 側(cè)的功能蛹磺,并獲取處理結(jié)果同仆。
EventChannel:用于向 Flutter 側(cè)傳遞事件,native 側(cè)主動(dòng)發(fā)消息給 Flutter俗或。
這三種類型比較相似,因?yàn)樗鼈兌际莻鬟f數(shù)據(jù)辛慰,實(shí)現(xiàn)方式也比較類似。
Channel 注冊
用官方的獲取電量的 Demo 來看看 Flutter 如何與 native通信帅腌。我們從調(diào)用處開始進(jìn)入 Flutter engine,一步步跟蹤代碼運(yùn)行過程戚篙。
在 iOS 項(xiàng)目中溺职,注冊獲取電量的 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));
}
......
}];
可以看到,首先初始化一個(gè)橋接乱灵,傳入橋接名和處理消息發(fā)送接收的類点待,橋接名為"samples.flutter.io/battery",處理消息的類為FlutterViewController癞埠。
通過setMethodCallHandler
方法,在 native 項(xiàng)目中注冊該橋接的回調(diào) handler颠区,其中參數(shù) result 表示向 Flutter 回傳的結(jié)果通铲。
- (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
颅夺,此處又包裝了一個(gè)回調(diào) messageHandler,用于解碼二進(jìn)制消息 message吧黄,并向 Flutter 側(cè)回傳執(zhí)行結(jié)果 reply。
- (void)setMessageHandlerOnChannel:(NSString*)channel
binaryMessageHandler:(FlutterBinaryMessageHandler)handler {
NSAssert(channel, @"The channel must not be null");
[_engine.get() setMessageHandlerOnChannel:channel binaryMessageHandler:handler];
}
FlutterViewController 不做處理廓八,將注冊事件轉(zhuǎn)發(fā)給 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 又將橋接事件轉(zhuǎn)發(fā)給 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_ 是個(gè)哈希表声功,key 是橋接名宠叼,value 放 handle先巴。原生注冊橋接方法冒冬,其實(shí)就是維護(hù)一個(gè) map 對(duì)象。
至此,注冊原生方法完成了证逻,整個(gè)流程如下
Flutter 調(diào)用 native 功能
先來看下 dart 側(cè)如何調(diào)用獲取電量的橋接方法
static const MethodChannel methodChannel =
MethodChannel('samples.flutter.io/battery');
final int result = await methodChannel.invokeMethod('getBatteryLevel');
跟蹤進(jìn)去
// 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 方法將方法名和參數(shù)轉(zhuǎn)化為二進(jìn)制數(shù)據(jù)囚企,并通過 BinaryMessages send 方法發(fā)送出去。
// 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 是一個(gè)map龙宏,存放需要 mock 的橋接,如果想攔截 mock 某個(gè)橋接银酗,會(huì)在這里處理。
不是 mock 的橋接蛙讥,則轉(zhuǎn)發(fā)到 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 將調(diào)用 native 的方法 Window_sendPlatformMessage。
dart 側(cè)的代碼跟蹤結(jié)束了翔曲,接下來又到了 native 側(cè)。
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 端調(diào)用傅蹂,其中 Window_sendPlatformMessage 被 dart 調(diào)用算凿,對(duì)應(yīng)的 _SendPlatformMessage 方法犁功。而 _SendPlatformMessage 又調(diào)用了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();
}
該方法最終會(huì)調(diào)用WindowClient的HandlePlatformMessage方法。
WindowClient是父類浸卦,由子類RuntimeController具體實(shí)現(xiàn),RuntimeController又將該消息交給其代理RuntimeDelegate處理靴庆,而RuntimeDelegate的具體實(shí)現(xiàn)則是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的實(shí)現(xiàn)是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 添加一個(gè) task稚叹,task 內(nèi)調(diào)用PlatformView(iOS中實(shí)現(xiàn)類為PlatfromViewIOS)的 HandlePlatformMessage 方法
// |shell::PlatformView|
void PlatformViewIOS::HandlePlatformMessage(fml::RefPtr<blink::PlatformMessage> message) {
platform_message_router_.HandlePlatformMessage(std::move(message));
}
把消息轉(zhuǎn)發(fā)給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對(duì)應(yīng)的 handle 并執(zhí)行
handle 完成后焰薄,將結(jié)果回調(diào)給 Flutter
流程如下
Native 調(diào)用 Flutter 功能
在 Flutter 側(cè),注冊監(jiān)聽扒袖,處理接收從 native 側(cè)發(fā)來的消息
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 側(cè)的全局map添加 key 為 name 的事件塞茅,當(dāng)接收到從 native 傳來的消息時(shí),找到就執(zhí)行對(duì)應(yīng) handle季率。
該方法向 native 側(cè)發(fā)送了一個(gè)名為 listen 的消息野瘦,這部分即是上面分析的 Flutter 調(diào)用 native 功能,不再贅述飒泻。
在 native 側(cè)鞭光,注冊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];
}
當(dāng)接收到從 dart 側(cè)發(fā)來的 listen 事件刹孔,取出對(duì)應(yīng)的 handle 并執(zhí)行啡省。
這個(gè) handle 將 FlutterEventSink 傳給 native 調(diào)用方。
FlutterEventSink 把參數(shù)編碼后傳給 FlutterViewController髓霞,F(xiàn)lutterViewController 依次轉(zhuǎn)發(fā)給 FlutterEngine卦睹、PlatformViewIOS,最終轉(zhuǎn)發(fā)到 RuntimeController DispatchPlatformMessage方库。向 dart 側(cè)發(fā)出該消息结序。
流程如下
參考資料:
Flutter
Flutter engine
深入理解Flutter Platform Channel
Flutter與Native通信 - PlatformChannel源碼分析