Platform Channel profile

Flutter introduces a Platform Channel mechanism to support API calls from different platforms. Flutter provides three Platform channels to support data transfer between platforms:

  • BasicMessageChannel: Supports string and semi-structured data transfer. You can use BasicMessageChannel to obtain resources such as ICONS of Native projects
  • MethodChannel: Supports passing method calls. A Flutter actively calls Native methods and gets the corresponding return value. Method calls to a Flutter can be made either from the Flutter developer platform or from the platform code
  • EventChannel: Supports data flow and transmits events. After receiving the message, the Dart cannot reply to the message, which is usually used for communication from Native to Dart

Method of use

BasicMessageChannel

The Android side:

BasicMessageChannel mBasicMessageChannel = new BasicMessageChannel(getFlutterView(), "basic_channel", StringCodec.INSTANCE);
mBasicMessageChannel.setMessageHandler(new BasicMessageChannel.MessageHandler() {/ / accept message @ Override public void onMessage (Object o, BasicMessageChannel. Reply Reply) {Log. E ("basic_channel"."Received message from Flutter :"+o.toString());
        reply.reply("Feed back."); }}); / / send a message mBasicMessageChannel. Send ("Send a message to Flutter"); / / send a message and accept the feedback mBasicMessageChannel flutter. Send ("Send a message to Flutter", new BasicMessageChannel.Reply() {
            @Override
            public void reply(Object o) {
                
            }
});
Copy the code

The Flutter end:

const basicMessageChannel = const BasicMessageChannel('basic_channel', StringCodec()); / / to accept and reply message basicMessageChannel. SetMessageHandler ((String message) = > Future < String > (() {setState(() {
              this.message = message;
            });
            return "Reply to native message"; })); Basicmessagechannel. send("Message from Flutter"); / / flutter didn't send and receive a Reply message ` send (T message, BasicMessageChannel. Reply < T > callback) ` methodCopy the code

MethodChannel

The Android side:

MethodChannel mMethodChannel = new MethodChannel(getFlutterView(), "method_channel");
mMethodChannel.setMethodCallHandler(new MethodChannel.MethodCallHandler() {Override public void onMethodCall(MethodCall, methodChannel.result Result) {if (methodCall.method.equals("noticeNative")) {
            todo()
            result.success("Accept success."); }}}); / / native calls to flutter mMethodChannel. InvokeMethod ("noticeFlutter"."argument", new MethodChannel.Result} @override public void error(String s,String s1, String s1, String s1) Object o) {// Callback failed} @override public voidnotImplemented() {}});Copy the code

The Flutter end:

const methodChannel = const MethodChannel('method_channel'); Future<Null> getMessageFromNative() {// async callback try {// callback success final String result = await methodChannel.invokeMethod('noticeNative');
      setState(() { method = result; }); } on PlatformException catch (e) {/ / callback failed}} methodChannel. SetMethodCallHandler ((MethodCall MethodCall) = > Future<String>(() {// respond to the native callif(methodCall.method == "noticeFlutter") {setState(() { }); }}));Copy the code

EventChannel

The Android side:

EventChannel eventChannel = new EventChannel(getFlutterView(),"event_channel");
eventChannel.setStreamHandler(new EventChannel.StreamHandler() {
    @Override
    public void onListen(Object o, EventChannel.EventSink eventSink) {
        eventSink.success("Success");
        //eventSink.error("Failure"."Failure"."Failure"); } @override public void onCancel(Object o) {// Call onCancel(Object o)}};Copy the code

The Flutter end:

const eventChannel = const EventChannel('event_channel'); eventChannel.receiveBroadcastStream().listen(_onEvent,onError:_onError); Void _onEvent(Object event) {void _onError(Object error) {// Return callback}Copy the code

Among them: Eventchannel. EventSink EventSink is the Native Dart callback function, EventSink provides three callback methods, success, Error, and endOfStream, corresponding to different event states respectively

Source, a preliminary study

Platform Channel basic structure

Let’s look at the code for the three channels:

BasicMessageChannel
class BasicMessageChannel<T> {
  const BasicMessageChannel(this.name, this.codec);
  final String name;
  final MessageCodec<T> codec;
  Future<T> send(T message) async {
    return codec.decodeMessage(await BinaryMessages.send(name, codec.encodeMessage(message)));
  }
  void setMessageHandler(Future<T> handler(T message)) {
    if (handler == null) {
      BinaryMessages.setMessageHandler(name, null);
    } else {
      BinaryMessages.setMessageHandler(name, (ByteData message) async {
        return codec.encodeMessage(await handler(codec.decodeMessage(message)));
      });
    }
  }
  void setMockMessageHandler(Future<T> handler(T message)) {
    if (handler == null) {
      BinaryMessages.setMockMessageHandler(name, null);
    } else {
      BinaryMessages.setMockMessageHandler(name, (ByteData message) async {
        returncodec.encodeMessage(await handler(codec.decodeMessage(message))); }); }}}Copy the code
MethodChannel
class MethodChannel {
  const MethodChannel(this.name, [this.codec = const StandardMethodCodec()]);
  final String name;
  final MethodCodec codec;
  void setMethodCallHandler(Future<dynamic> handler(MethodCall call)) {
    BinaryMessages.setMessageHandler(
      name,
      handler == null ? null : (ByteData message) => _handleAsMethodCall(message, handler),
    );
  }
  void setMockMethodCallHandler(Future<dynamic> handler(MethodCall call)) {
    BinaryMessages.setMockMessageHandler(
      name,
      handler == null ? null : (ByteData message) => _handleAsMethodCall(message, handler),
    );
  }
  Future<ByteData> _handleAsMethodCall(ByteData message, Future<dynamic> handler(MethodCall call)) async {
    final MethodCall call = codec.decodeMethodCall(message);
    try {
      return codec.encodeSuccessEnvelope(await handler(call));
    } on PlatformException catch (e) {
      returun ...
    } on MissingPluginException {
      return null;
    } catch (e) {
      return. } } Future<T> invokeMethod<T>(String method, [dynamic arguments]) async { assert(method ! = null); final ByteData result = await BinaryMessages.send( name, codec.encodeMethodCall(MethodCall(method, arguments)), );if (result == null) {
      throw MissingPluginException('No implementation found for method $method on channel $name');
    }
    final T typedResult = codec.decodeEnvelope(result);
    returntypedResult; }}Copy the code
EventChannel
class EventChannel {
  const EventChannel(this.name, [this.codec = const StandardMethodCodec()]);
  final String name;
  final MethodCodec codec;
  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 {
        ...
      });
      try {
        await methodChannel.invokeMethod<void>('listen', arguments);
      } catch (exception, stack) {
        ...
      }
    }, onCancel: () async {
      BinaryMessages.setMessageHandler(name, null);
      try {
        await methodChannel.invokeMethod<void>('cancel', arguments); } catch (exception, stack) { ... }});returncontroller.stream; }}Copy the code

Each of these channels has two member variables:

  • Name: A unique identifier used to distinguish Platform channels. Each Channel uses a unique name as its unique identifier
  • Codec: A message codec that uses a binary byte stream as the data transfer protocol for Flutter: the sender encodes the data into binary and the receiver decodes the data into raw data. Codec is responsible for encoding and decoding. It’s used in every ChannelBinaryMessages, it acts as a messenger, responsible for carrying information across platforms, and is a tool for sending and receiving messages.
setMessageHandler

After creating BasicMessageChannel, BinaryMessenger calls its setMessageHandler method to set up a message handler for the BasicMessageChannel to process and respond to messages from another platform.

send

Once you have created BasicMessageChannel, you can call its send method to pass data to another platform.

setMethodCallHandler

Sets the callback to receive a method call on this MethodChannel

receiveBroadcastStream

Sets up a broadcast stream to receive events on this EventChannel

Handler

A Flutter uses a Handler to process the message decoded by Codec. Corresponding to the three Platform channels, three types of handlers are defined in Flutter:

  • MessageHandler: Used to process string or semi-structured messages, defined in BasicMessageChannel.
  • MethodCallHandler: Used to handle method calls, defined in MethodChannel.
  • StreamHandler: Used for event flow messages, defined in EventChannel

When using a Platform Channel, you need to register a BinaryMessageHandler for it and set the corresponding Handler for it. Binary data is processed by BinaryMessageHanler, first decoded using Codec, and then distributed to specific handlers for processing.

conclusion

To learn more about The design and implementation of Platform Channel, you can go to the inside story of Flutter technology :Platform Channel Design and Implementation, follow the Great God blog