Flutter 與 Native(iOS) 通訊原理

Flutter 與 Native 通訊原理ios

Flutter 是一個跨平臺開發框架,它使用了一種全新的方式,本身重寫了一個平臺無關的渲染引擎,它只提供畫布,全部的 UI 組件、渲染邏輯都是在這個引擎上處理的。但某些平臺獨有的功能特性,仍由平臺本身處理,Flutter 提供了 Platform Channel 機制,讓消息可以在 native 與 Flutter 之間進行傳遞。git

接下來咱們將深刻 Flutter engine 內部,看看 Flutter 是如何調用 native 模塊,native 如何調用 Flutter,數據是如何在兩端傳遞。github

Platform Channel

每一個 Channel 都有一個獨一無二的名字,Channel 之間經過 name 區分彼此。shell

Channel 使用 codec 消息編解碼器,支持從基礎數據到二進制格式數據的轉換、解析。bash

Channel 有三種類型,分別是: BasicMessageChannel:用於傳遞基本數據 MethodChannel: 用於傳遞方法調用,Flutter 側調用 native 側的功能,並獲取處理結果。 EventChannel:用於向 Flutter 側傳遞事件,native 側主動發消息給 Flutter。 這三種類型比較類似,由於它們都是傳遞數據,實現方式也比較相似。app

Channel 註冊

用官方的獲取電量的 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 對象。

至此,註冊原生方法完成了,整個流程以下

image.png

Flutter 調用 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.png

Native 調用 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源碼分析

相關文章
相關標籤/搜索