Flutter 与 Native(iOS) 通信原理

96
潇潇潇潇潇潇潇
1.5 2019.01.15 17:46 字数 1200

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.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源码分析

Flutter
Gupao