Flutter 與 Native 通訊原理ios
Flutter 是一個跨平臺開發框架,它使用了一種全新的方式,本身重寫了一個平臺無關的渲染引擎,它只提供畫布,全部的 UI 組件、渲染邏輯都是在這個引擎上處理的。但某些平臺獨有的功能特性,仍由平臺本身處理,Flutter 提供了 Platform Channel 機制,讓消息可以在 native 與 Flutter 之間進行傳遞。git
接下來咱們將深刻 Flutter engine 內部,看看 Flutter 是如何調用 native 模塊,native 如何調用 Flutter,數據是如何在兩端傳遞。github
每一個 Channel 都有一個獨一無二的名字,Channel 之間經過 name 區分彼此。shell
Channel 使用 codec 消息編解碼器,支持從基礎數據到二進制格式數據的轉換、解析。bash
Channel 有三種類型,分別是: BasicMessageChannel:用於傳遞基本數據 MethodChannel: 用於傳遞方法調用,Flutter 側調用 native 側的功能,並獲取處理結果。 EventChannel:用於向 Flutter 側傳遞事件,native 側主動發消息給 Flutter。 這三種類型比較類似,由於它們都是傳遞數據,實現方式也比較相似。app
用官方的獲取電量的 Demo 來看看 Flutter 如何與 native通訊。咱們從調用處開始進入 Flutter engine,一步步跟蹤代碼運行過程。框架
在 iOS 項目中,註冊獲取電量的 channelasync
- (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。ui
經過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 對象。
至此,註冊原生方法完成了,整個流程以下
先來看下 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
流程以下
在 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 側發出該消息。
流程以下
參考資料: Flutter Flutter engine 深刻理解Flutter Platform Channel Flutter與Native通訊 - PlatformChannel源碼分析