Flutter MethodChannel分析

Posted 一叶飘舟

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flutter MethodChannel分析相关的知识,希望对你有一定的参考价值。

一、三种Channel

Flutter中通过Platform Channel实现Flutter和原生端的数据传递,那么这些数据是怎么传递的,传递的过程都做了哪些操作

Flutter定义了三种不同类型的Channel,分别是

  1. BasicMessageChannel:用于传递字符串和半结构化的数据;
  2. MethodChannel:用于传递方法调用;
  3. EventChannel:用于数据流的通信;

官方大图:

二、MethodChannel实现原理

  • android端分析MethodChannel的实现原理,入口选在使用MethodChannel的使用的地方是最合适的。

2.1、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) 

      
    );
    
//3、主动call flutter的方法
    channel.invokeMethod("callFlutter", "params", new MethodChannel.Result() 
      @Override
      public void success(Object result) 
        //call success
      

      @Override
      public void error(String errorCode, String errorMessage, Object errorDetails) 
        //call fail
      

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

2.2、源码分析

2.2.1、MethodChannel的构造函数

    public MethodChannel(BinaryMessenger messenger, String name) 
        this(messenger, name, StandardMethodCodec.INSTANCE);
    
  • 入参是一个通讯BinaryMessenger, 专门和Flutter进行通信的一个二进制异步消息接口;
  • 使用的时候通常是传入一个FlutterView,因为FlutterView实现了这个接口。

2.2.2、Android到Flutter消息发送过程

    public void invokeMethod(String method, @Nullable Object arguments, Result callback) 
        messenger.send(name, codec.encodeMethodCall(new MethodCall(method, arguments)),
            callback == null ? null : new IncomingResultHandler(callback));
    
  • 最终是使用构造函数传入的BinaryMessenger实现类来发送消息,发送的信息有三个,

    (1)通道的名字 (2)方法名字加上参数经过编码转化为一个bytebuff (3)方法回调IncomingResultHandler()

FlutterView的发送方法

  public void send(String channel, ByteBuffer message, BinaryReply callback) 
        if (!isAttached()) 
            Log.d(TAG, "FlutterView.send called on a detached view, channel=" + channel);
            return;
        
        mNativeView.send(channel, message, callback);
    
复制代码
  • 调用了变量mNativeView的发送方法,mNativeView是一个FlutterNativeView,继续跟进

FlutterNativeView的发送方法

 public void send(String channel, ByteBuffer message, BinaryReply callback) 
        if (!isAttached()) 
            Log.d(TAG, "FlutterView.send called on a detached view, channel=" + channel);
            return;
        

        dartExecutor.getBinaryMessenger().send(channel, message, callback);
    
复制代码
  • 它从dartExecutor中获取到一个BinaryMessager,然后调用了它的发送方法。
//DartExecutor.java
//部分代码
 @NonNull
  private final BinaryMessenger binaryMessenger;
  
  public DartExecutor(@NonNull FlutterJNI flutterJNI, @NonNull AssetManager assetManager) 
    this.flutterJNI = flutterJNI;
    this.assetManager = assetManager;
    this.dartMessenger = new DartMessenger(flutterJNI);
    dartMessenger.setMessageHandler("flutter/isolate", isolateChannelMessageHandler);
    this.binaryMessenger = new DefaultBinaryMessenger(dartMessenger);
  
  
  public BinaryMessenger getBinaryMessenger() 
    return binaryMessenger;
  
复制代码
  • 很明显是调用了BinaryMessenger的发送方法,看一下DefaultBinaryMessenger

DefaultBinaryMessenger的发送方法

private static class DefaultBinaryMessenger implements BinaryMessenger 
    private final DartMessenger messenger;

    private DefaultBinaryMessenger(@NonNull DartMessenger messenger) 
      this.messenger = messenger;
    

    public void send(@NonNull String channel, @Nullable ByteBuffer message) 
      messenger.send(channel, message, null);
    

 
    public void send(@NonNull String channel, @Nullable ByteBuffer message, @Nullable BinaryMessenger.BinaryReply callback) 
      messenger.send(channel, message, callback);
    

    public void setMessageHandler(@NonNull String channel, @Nullable BinaryMessenger.BinaryMessageHandler handler) 
      messenger.setMessageHandler(channel, handler);
    
  
复制代码
  • DefaultBinaryMessenger就是对DartMessenger的一个封装,啥都没有做。

DartMessenger的发送过程

 @Override
  @UiThread
  public void send(@NonNull String channel, @NonNull ByteBuffer message) 
    Log.v(TAG, "Sending message over channel '" + channel + "'");
    send(channel, message, null);
  

  @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);//维护一个replyId为key,callback为value的hashmap
    
    if (message == null) 
      flutterJNI.dispatchEmptyPlatformMessage(channel, replyId);
     else 
      flutterJNI.dispatchPlatformMessage(channel, message, message.position(), replyId);
    
  
  • 如果有方法回调callback,就会生成一个replyId,然后把方法回调和replyId加入到一个map;
  • 如果没有就直接发送消息了。
  • 如果消息体为空,就发送一个空消息;
  • 如果有消息内容,就把消息发送过去。还会携带一个replyId,这个会在回调的时候有用。

FlutterJni分发消息

 public void dispatchPlatformMessage(@NonNull String channel, @Nullable ByteBuffer message, int position, int responseId) 
    ensureRunningOnMainThread();//确保在主线程执行
    if (isAttached()) 
      nativeDispatchPlatformMessage(
          nativePlatformViewId,
          channel,
          message,
          position,
          responseId
      );
     else 
      Log.w(TAG, "Tried to send a platform message to Flutter, but FlutterJNI was detached from native C++. Could not send. Channel: " + channel + ". Response ID: " + responseId);
    
  

// Send a data-carrying platform message to Dart.
  private native void nativeDispatchPlatformMessage(
      long nativePlatformViewId,
      @NonNull String channel,
      @Nullable ByteBuffer message,
      int position,
      int responseId
  );
  • 最终调到了c++层面,通过c++的dart虚拟机,把消息传递给了flutter。

2.2.3、flutter向原生传递消息

已经确定了消息是通过c++层面的虚拟机,发送到flutter,那么flutter的消息肯定也是要从c++层面的虚拟机发送过来,c++虚拟机就是原生与flutter通讯的桥梁。

android与c++的沟通肯定是通过FlutterJni这个类。

FlutterJni层做了什么

//FlutterJni讲自己绑定到了c++,
  public void attachToNative(boolean isBackgroundView) 
    ensureRunningOnMainThread();
    ensureNotAttachedToNative();
    nativePlatformViewId = nativeAttach(this, isBackgroundView);
  

  private native long nativeAttach(@NonNull FlutterJNI flutterJNI, boolean isBackgroundView);
  • 把java类FlutterJNI传入jni
  • 为了回调用java类中的方法
//处理从flutter主动call过来的消息
//Called by native on the UI thread
  private void handlePlatformMessage(@NonNull final String channel, byte[] message, final int replyId) 
    if (platformMessageHandler != null) 
      platformMessageHandler.handleMessageFromDart(channel, message, replyId);
    
  

//用来处理方法调用到flutter以后的回调
  private void handlePlatformMessageResponse(int replyId, byte[] reply) 
    if (platformMessageHandler != null) 
      platformMessageHandler.handlePlatformMessageResponse(replyId, reply);
    
  
  • 最终调动的了platformMessageHandler的分发的方法;

PlatformMessageHandler

//通过这个方法绑定了一个方法的回调;
 public void setPlatformMessageHandler(@Nullable PlatformMessageHandler platformMessageHandler) 
    ensureRunningOnMainThread();
    this.platformMessageHandler = platformMessageHandler;
  
  • PlatformMessageHandler是一个接口,处理从Dart层call的消息
  • DartMessenger是他的实现类,再看一下DartExecutor的一些方法;

DartExecutor

public DartExecutor(@NonNull FlutterJNI flutterJNI, @NonNull AssetManager assetManager) 
    this.flutterJNI = flutterJNI;
    this.assetManager = assetManager;
    this.dartMessenger = new DartMessenger(flutterJNI);
    dartMessenger.setMessageHandler("flutter/isolate", isolateChannelMessageHandler);
    this.binaryMessenger = new DefaultBinaryMessenger(dartMessenger);
  

  public void onAttachedToJNI() 
    Log.v(TAG, "Attached to JNI. Registering the platform message handler for this Dart execution context.");
    flutterJNI.setPlatformMessageHandler(dartMessenger);
  
  • 在构造函数里面,构建DartMessager的对象,然后在调动onAttachToJNI的时候,注册给了FlutterJNI类。
  • 所以方法的处理是在DartMessager这个类中

分支一、DartMessager中处理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);
      
    
  

先看下面那个方法,这个是原生调用flutter以后的回复。里面有一个replayId,和reply字节数组。

  1. 首先根据replyid找到刚才发送消息时保存的callback,
  2. 如果找到了,就把reply转化为bytebuff调用出去。
  3. BinaryReply的实现类为IncomingResultHandler

MethodChannel中IncomingResultHandler分析

 private final class IncomingResultHandler implements BinaryReply 
        private final Result callback;

        IncomingResultHandler(Result callback) 
            this.callback = callback;
        

        @Override
        @UiThread
        public void reply(ByteBuffer reply) 
            try 
                if (reply == null) 
                    callback.notImplemented();
                 else 
                    try 
                        callback.success(codec.decodeEnvelope(reply));
                     catch (FlutterException e) 
                        callback.error(e.code, e.getMessage(), e.details);
                    
                
             catch (RuntimeException e) 
                Log.e(TAG + name, "Failed to handle method call result", e);
            
        
    
  • IncomingResultHandler包装了Result,最终消息在IncomingResultHandler#reply中解码后,传给了callback。
  • 我们从原生调用一个方法,然后获得结果的整个过程就是这样子。
  • 如果是从flutter主动调动的消息,则是另一个线路。在DartMessage#handleMessageFromDart这个方法中,处理了flutter主动的消息。

分支二、DartMessager中处理flutter的主动调用原生信息

@Override
//三个参数,通道名字,消息的字节,从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 + "'");
    
    //1、根据通道名字找到对象的处理类
    BinaryMessenger.BinaryMessageHandler handler = messageHandlers.get(channel);
    final DartMessengerTaskQueue taskQueue = (handlerInfo != null) ? handlerInfo.taskQueue : null;
    Runnable myRunnable =
        () -> 
          Trace.beginSection("DartMessenger#handleMessageFromDart on " + channel);
          try 
            invokeHandler(handlerInfo, message, replyId);
            if (message != null && message.isDirect()) 
              // This ensures that if a user retains an instance to the ByteBuffer and it happens to
              // be direct they will get a deterministic error.
              message.limit(0);
            
           finally 
            // This is deleting the data underneath the message object.
            flutterJNI.cleanupMessageData(messageData);
            Trace.endSection();
          
        ;
  
  
  private void invokeHandler(
      @Nullable HandlerInfo handlerInfo, @Nullable ByteBuffer message, final int replyId) 
    // Called from any thread.
    if (handlerInfo != null) 
      try 
        //转发给注册的handler去处理消息
        handlerInfo.handler.onMessage(message, new Reply(flutterJNI, replyId));
       catch (Exception ex) 
        Log.e(TAG, "Uncaught exception in binary message listener", ex);
        flutterJNI.invokePlatformMessageEmptyResponseCallback(replyId);
       catch (Error err) 
        handleError(err);
      
     else 
      Log.v(TAG, "No registered handler for message. Responding to Dart with empty reply message.");
      flutterJNI.invokePlatformMessageEmptyResponseCallback(replyId);
    
  
  • 最终回调到MethodChannel中IncomingMethodCallHandler中

2.2.4、handler是什么时候注册的?

  • 从MethodChannel中的注册来看
    //MethodChannel
    public void setMethodCallHandler(final @Nullable MethodCallHandler handler) 
        //这个messager是flutterView
        messenger.setMessageHandler(name,
            handler == null ? null : new IncomingMethodCallHandler(handler));
    
    
   //FlutterView.java
   public void setMessageHandler(String channel, BinaryMessageHandler handler) 
        //mNativeView 是FlutterNativeView
        mNativeView.setMessageHandler(channel, handler);
    
    //FlutterNativeView.java
    public void setMessageHandler(String channel, BinaryMessageHandler handler) 
        dartExecutor.getBinaryMessenger().setMessageHandler(channel, handler);
    
// DartMessage.java
public void setMessageHandler(@NonNull String channel, @Nullable BinaryMessenger.BinaryMessageHandler handler) 
    if (handler == null) 
      Log.v(TAG, "Removing handler for channel '" + channel + "'");
      messageHandlers.remove(channel);
     else 
      Log.v(TAG, "Setting handler for channel '" + channel + "'");
      messageHandlers.put(channel, handler);
    
  
  • 最终又到了DartExecutor中,从上面的分析可以知道, dartExecutor.getBinaryMessenger()获得的是一个 DefaultBinaryMessenger;
  • 而DefaultBinaryMessenger中持有了DartMessager。
  • 所以setMessageHandler最终被设置到DartMessage中。
  • 所以从c++层面来了消息以后,就会回调到MethodChannel中;
  • 在MethodChannel中设置的是一个MethodCallHandler,最后被包装为实现了BinaryMessageHandler的IncomingMethodCallHandler
private final class IncomingMethodCallHandler implements BinaryMessageHandler 
        private final MethodCallHandler handler;

        IncomingMethodCallHandler(MethodCallHandler handler) 
            this.handler = handler;
        
        "DartMessage 调用这个方法把消息传递过来"
        @Override
        @UiThread
        public void onMessage(ByteBuffer message, final BinaryReply reply) 
        
            //1、解码信息MethodCall中包含了方法名字和参数
            final MethodCall call = codec.decodeMethodCall(message);
            try 
                //2、调用回调处理,如果需要回传结果,就调用Result的success方法。"
                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);
                    
                );
             catch (RuntimeException e) 
                Log.e(TAG + name, "Failed to handle method call", e);
                reply.reply(codec.encodeErrorEnvelope("error", e.getMessage(), null));
            
        
    

2.3、从Dart角度分析

用一张图说明,不做源码分析了

三、时序图

从Flutter调用原生开始 

原生返回Flutter数据

参考大佬两幅图

以上是关于Flutter MethodChannel分析的主要内容,如果未能解决你的问题,请参考以下文章

MethodChannel 实现flutter 与 原生通信

FlutterFlutter 混合开发 ( Flutter 与 Native 通信 | Android 端实现 MethodChannel 通信 )

flutter-MethodChannel的后台侦听器

Flutter - 无法将 MethodChannel 与 Firebase onBackgroundMessage 一起使用

Flutter MethodChannel 直接分享到 Whatsapp

Flutter通过MethodChannel实现Flutter 与Android iOS 的双向通信