flutter message channel原理

flutter message channel原理

最近在学习flutter插件,对flutter ui层与native层的通信机制进行了了解,记录下对message channel通信机制的理解。

1 为什么要使用message channel?

如果app只涉及到ui渲染以及http请求的话,只使用flutter的ui 框架和http请求框架就可以了,但当需要使用到一些native原生技术支撑,flutter ui框架无法直接调用系统提供的原生接口,这个时候就需要使用flutter的插件来为flutter ui框架与native之间建立通信,message channel就是flutter插件的核心。

2 flutter插件的工作流程

(1)app在启动的时候会由flutter引擎加载flutter插件。

/**
 * Generated file. Do not edit.
 * This file is generated by the Flutter tool based on the
 * plugins that support the Android platform.
 */
@Keep
public final class GeneratedPluginRegistrant {
  public static void registerWith(@NonNull FlutterEngine flutterEngine) {
    flutterEngine.getPlugins().add(new com.example.flutter_plugin_battery.FlutterPluginBatteryPlugin());
  }
}

(2)flutter插件在加载后会回调onAttachedToEngine,并传递FlutterPluginBinding ,通过FlutterPluginBinding 可以获取到flutter引擎提供的能力。在onAttachedToEngine中创建了MethodChannel 和EventChannel ,并设置了setMethodCallHandler和setStreamHandler回调。

/** FlutterPluginBatteryPlugin */
public class FlutterPluginBatteryPlugin implements FlutterPlugin, MethodCallHandler, EventChannel.StreamHandler {
  /// The MethodChannel that will the communication between Flutter and native Android
  ///
  /// This local reference serves to register the plugin with the Flutter Engine and unregister it
  /// when the Flutter Engine is detached from the Activity
  private MethodChannel channel;
  private EventChannel eventChannel;
  private FlutterPluginBinding flutterPluginBinding;
  

  @Override
  public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
    this.flutterPluginBinding = flutterPluginBinding;
    channel = new MethodChannel(flutterPluginBinding.getFlutterEngine().getDartExecutor(), "flutter_plugin_battery");
    eventChannel = new EventChannel(flutterPluginBinding.getFlutterEngine().getDartExecutor().getBinaryMessenger(),"event_battery_event");
    channel.setMethodCallHandler(this);
    eventChannel.setStreamHandler(this);
  }
  

  @Override
  public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
    if (call.method.equals("getPlatformVersion")) {
      result.success("Android " + android.os.Build.VERSION.RELEASE);
    }
    if (call.method.equals("getBattertLever")){
      if(flutterPluginBinding != null){
        Context context = flutterPluginBinding.getApplicationContext();
        BatteryManager batteryManager = (BatteryManager)context.getSystemService(BATTERY_SERVICE);
        int battery = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
        result.success(battery);
      }
    }
  }

  @Override
  public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
    this.flutterPluginBinding = null;
    channel.setMethodCallHandler(null);
    eventChannel.setStreamHandler(null);
  }

  @Override
  public void onListen(Object arguments, final EventChannel.EventSink events) {
   events.success(1);
  }

  @Override
  public void onCancel(Object arguments) {
  }
  
}

(3)在 flutter ui层提供给ui层调用的接口。可以看到,MethodChannel(‘flutter_plugin_battery’)与EventChannel(“event_battery_event”)中设置的名字与java插件层设置的名字是一致的。当调用platformVersion方法的时候,java插件层的onMethodCall会调用并执行相应的方法。

import 'dart:async';

import 'package:flutter/services.dart';

class FlutterPluginBattery {
  static const MethodChannel _channel =
      const MethodChannel('flutter_plugin_battery');

  static const EventChannel _eventChannel = EventChannel("event_battery_event");

  static Future<String> get platformVersion async {
    final String version = await _channel.invokeMethod('getPlatformVersion');
    return version;
  }

  static Future<int> get batteryLever async{
    int batteryLever = await _channel.invokeMethod('getBattertLever');
    return batteryLever;
  }

  static Stream<dynamic> get onBatteryChanger{
    return _eventChannel.receiveBroadcastStream();
  }

}

3 上述过程的原理是什么?

(1) methodChannel的原理

当在flutter ui层创建methodChannel的时候,先看下methodChannel的构造函数。name就是传入的mechodChannel的唯一的名字,codec和binaryMessenger是可选参数,分别是方法编码器和通道信使。

  const MethodChannel(this.name, [this.codec = const StandardMethodCodec(), BinaryMessenger binaryMessenger ])
      : assert(name != null),
        assert(codec != null),
        _binaryMessenger = binaryMessenger;

  /// The logical channel on which communication happens, not null.
  final String name;

  /// The message codec used by this channel, not null.
  final MethodCodec codec;

接着看methodChannel的_invokeMethod方法,关键看binaryMessenger.send(
name,
codec.encodeMethodCall(MethodCall(method, arguments)),
);
,MethodCall封装了调用方法与参数,通过codec.encodeMethodCall方法将调用方法与参数转化为二进制流,最后通过binaryMessenger.send方法将通道name与二进制流发送给flutter引擎。

  Future<T> _invokeMethod<T>(String method, { bool missingOk, dynamic arguments }) async {
    assert(method != null);
    final ByteData result = await binaryMessenger.send(
      name,
      codec.encodeMethodCall(MethodCall(method, arguments)),
    );
    if (result == null) {
      if (missingOk) {
        return null;
      }
      throw MissingPluginException('No implementation found for method $method on channel $name');
    }
    return codec.decodeEnvelope(result) as T;
  }

flutter引擎收到消息后会查找与name对用的BinaryMessageHandler,BinaryMessageHandler是由java插件层设置回调设置的。

  public void setMethodCallHandler(final @Nullable MethodCallHandler handler) {
    messenger.setMessageHandler(
        name, handler == null ? null : new IncomingMethodCallHandler(handler));
  }

找到后,会通过jni反向调用java插件层的回调对象并调用
onMessage(ByteBuffer message, final BinaryReply reply)方法,其中,message表示封装的二进制方法流,reply是flutter引擎封装的对象,通过该对象可以将结果反馈给ui框架层的方法调用对象。codec.decodeMethodCall(message)会将二进制流解码成MethodCall 对象提供给上层使用。

@Override
    @UiThread
    public void onMessage(ByteBuffer message, final BinaryReply reply) {
      final MethodCall call = codec.decodeMethodCall(message);
      try {
        handler.onMethodCall(
            call,
            new Result() {
              @Override
              public void success(Object result) {
                reply.reply(codec.encodeSuccessEnvelope(result));
              }

              @Override
              public void error(String errorCode, String errorMessage, Object errorDetails) {
                reply.reply(codec.encodeErrorEnvelope(errorCode, errorMessage, errorDetails));
              }

              @Override
              public void notImplemented() {
                reply.reply(null);
              }
            });
      } 

这样,flutter ui层调用native层的流程就通了。

(2)EventChannel 的原理

在flutter ui 层调用eventChannel的receiveBroadcastStream方法后,首先创建了MethodChannel,设置messageHandler回调,用于接收methodChannel的消息。在这里我纠结了很久,这个messageHandler会不会接受到flutter ui层和插件层发送的channel 消息呢?我猜是不会的,因为这里设置的并不是binaryMessageHandler ,flutter 引擎应该是在BinaryMessager 中寻找对应channel 的 binaryMessageHandler 来处理channel message 的。因此这里应该只是设置了binaryMessager 对应channel 的reply回调,用于接收 flutter 插件层的reply消息。

同时调用invokeMethod(‘listen’, arguments)方法。、

Stream<dynamic> receiveBroadcastStream([ dynamic arguments ]) {
    final MethodChannel methodChannel = MethodChannel(name, codec);
    StreamController<dynamic> controller;
    controller = StreamController<dynamic>.broadcast(onListen: () async {
      binaryMessenger.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<void>('listen', arguments);
      } catch (exception, stack) {
        FlutterError.reportError(FlutterErrorDetails(
          exception: exception,
          stack: stack,
          library: 'services library',
          context: ErrorDescription('while activating platform stream on channel $name'),
        ));
      }
    }, onCancel: () async {
      binaryMessenger.setMessageHandler(name, null);
      try {
        await methodChannel.invokeMethod<void>('cancel', arguments);
      } catch (exception, stack) {
        FlutterError.reportError(FlutterErrorDetails(
          exception: exception,
          stack: stack,
          library: 'services library',
          context: ErrorDescription('while de-activating platform stream on channel $name'),
        ));
      }
    });
    return controller.stream;
  }

在调用Listen方法后会调用java插件层的onListen方法,并传递EventChannel.EventSink events对象。

    @Override
    public void onMessage(ByteBuffer message, final BinaryReply reply) {
      final MethodCall call = codec.decodeMethodCall(message);
      if (call.method.equals("listen")) {
        onListen(call.arguments, reply);
      } else if (call.method.equals("cancel")) {
        onCancel(call.arguments, reply);
      } else {
        reply.reply(null);
      }
    }

来看下EventChannel.EventSink对象的success和error方法,最终会通过BinaryMessage发送消息给MethodChannel,flutter引擎找到messageHandler 的onMessage方法,发现是调用了 reply.reply(null),将消息发送给了flutter ui框架设置的reply,这样,就监听到了插件层的消息。

@UiThread
      public void success(Object event) {
        if (hasEnded.get() || activeSink.get() != this) {
          return;
        }
        EventChannel.this.messenger.send(name, codec.encodeSuccessEnvelope(event));
      }

      @Override
      @UiThread
      public void error(String errorCode, String errorMessage, Object errorDetails) {
        if (hasEnded.get() || activeSink.get() != this) {
          return;
        }
        EventChannel.this.messenger.send(
            name, codec.encodeErrorEnvelope(errorCode, errorMessage, errorDetails));
      }

(3)PlatformView的原理

我这边理解的是flutter 通过platformView 在flutter ui 同一层创建了一块surface用于原生view的渲染,然后通过methodChannel来控制原生组件执行方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值