FlutterPlugin的探索

本文主要是介绍FlutterPlugin,涉及到原理和使用。

Flutter Plugin提供Android或者iOS的底层封装,在Flutter层提供组件功能,使Flutter可以较方便的调取Native的模块。很多平台相关性或者对于Flutter实现起来比较复杂的部分,都可以封装成Plugin。

那么flutter和native如何相互调用呢,原理如下


消息在client和host之间通过平台通道(platform channels)来进行的,之间的通讯都是异步的。

我们先介绍下MethodChannel的实现原理,使用方式以Android端为例:

//1、注册通道
    MethodChannel channel = new MethodChannel(getFlutterView(),"flutter/channel");

//2、设置回调,被call回调
    channel.setMethodCallHandler(new MethodChannel.MethodCallHandler() {
      @Override
      public void onMethodCall(MethodCall call, MethodChannel.Result result) {  
              //通知到flutter端
              result.success(“返回值”);
      }
    });
    
//3、主动call flutter的方法
    channel.invokeMethod("callFlutter", "params", new MethodChannel.Result() {
      @Override
      public void success(Object result) {
        //呼叫成功
      }

      @Override
      public void error(String errorCode, String errorMessage, Object errorDetails) {
        //呼叫出现异常
      }

      @Override
      public void notImplemented() {
        //flutter没有对应的实现方法
      }
    });

其实MethodChannel中的构造函数第一个参数就是BinaryMessenger,这个BinaryMessenger是在DartExecuter中初始化的,而BinaryMessenger的实现类中的send函数最终是对DartMessenger的一层包装,所以我们看消息是如何发送出去的,直接看DartMessengersend函数

  @Override
  public void send(
      @NonNull String channel,
      @Nullable ByteBuffer message,
      @Nullable BinaryMessenger.BinaryReply callback
  ) {
    Log.v(TAG, "Sending message with callback over channel '" + channel + "'");
    int replyId = 0;
    if (callback != null) {
      replyId = nextReplyId++;
      pendingReplies.put(replyId, callback);
    }
    if (message == null) {
      flutterJNI.dispatchEmptyPlatformMessage(channel, replyId);
    } else {
      flutterJNI.dispatchPlatformMessage(channel, message, message.position(), replyId);
    }
  }

如果有方法回调callback,就会生成一个replyId,然后把方法回调和replyId加入到一个map,如果没有就直接发送消息了。
如果消息体为空,就发送一个空消息,如果有消息内容,就把消息发送过去。还会携带一个replyId,这个会在回调的时候有用。发送消息是通过flutterJni分发方法,最终调到了c++层面,通过c++的dart虚拟机,把消息传递给了flutter。

如何收到flutter端的消息呢
同样的接受消息也是在DartExecutor,通过onAttachToJNI会建立通道关联,同样的消息会经过DartMessenger,如果是收到原生调用flutter以后的回复,会调用

  @Override
  public void handlePlatformMessageResponse(int replyId, @Nullable byte[] reply) {
    Log.v(TAG, "Received message reply from Dart.");
    BinaryMessenger.BinaryReply callback = pendingReplies.remove(replyId);
    if (callback != null) {
      try {
        Log.v(TAG, "Invoking registered callback for reply from Dart.");
        callback.reply(reply == null ? null : ByteBuffer.wrap(reply));
      } catch (Exception ex) {
        Log.e(TAG, "Uncaught exception in binary message reply handler", ex);
      }
    }
  }

首先根据replyId找到刚才发送消息时保存的callback,找到了,就把reply转化为bytebuff调用出去。BinaryReply会将消息回调回去。
如果是从flutter主动调动的消息稍微负责一些,也是类似的原理,只不过多了一步根据通道名字找到对象的处理类。

//三个参数,通道名字,消息的字节,从flutter端传递过来的replyId,
  public void handleMessageFromDart(
      @NonNull final String channel,
      @Nullable byte[] message,
      final int replyId
  ) {
    Log.v(TAG, "Received message from Dart over channel '" + channel + "'");
    
    //根据通道名字找到对象的处理类"
    BinaryMessenger.BinaryMessageHandler handler = messageHandlers.get(channel);
    if (handler != null) {
      try {
        Log.v(TAG, "Deferring to registered handler to process message.");
        
       //解码数据并调用onMessage传递消息 ,注意一下这个Reply
        final ByteBuffer buffer = (message == null ? null : ByteBuffer.wrap(message));
        handler.onMessage(buffer, new Reply(flutterJNI, replyId));
      } catch (Exception ex) {
        Log.e(TAG, "Uncaught exception in binary message listener", ex);
        //异常处理
        flutterJNI.invokePlatformMessageEmptyResponseCallback(replyId);
      }
    } else {
      Log.v(TAG, "No registered handler for message. Responding to Dart with empty reply message.");
      //未实现通道处理
      flutterJNI.invokePlatformMessageEmptyResponseCallback(replyId);
    }
  }

像Flutter官方提供的shared_preferences,就是通过通道来实现的。

FlutterPlugin还有另外一个用法,就是直接在Flutter端由Native渲染。比如视频播放插件video_player。
通常有两种做法,一种是PlatformView,另外一种是Texture(俗称外接纹理)。其中PlatformView区分Android和iOS,在Android平上上叫做 AndroidView,而在iOS平台,叫UIKitView。
前面提到的MethodChannel可以向flutter传输数据,但是如果将一个视频或者图片的数据传到flutter会很繁重,所以Flutter提供了一种基于Texture的数据共享机制,而以上两种方式原理都是基于Texture。

Texture https://api.flutter.dev/flutter/widgets/Texture-class.html

下图是视频插件的实现原理图


你可能感兴趣的:(FlutterPlugin的探索)