美文网首页
FlutterPlugin的探索

FlutterPlugin的探索

作者: 弘法大师 | 来源:发表于2020-11-13 18:31 被阅读0次

    本文主要是介绍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的探索

          本文链接:https://www.haomeiwen.com/subject/zmkbbktx.html