Flutter MethodChannel分析

1. Three Channels

In Flutter, the data transfer between Flutter and the native side is realized through the Platform Channel, so how is the data transferred, and what operations have been done during the transfer process

Flutter defines three different types of Channels, namely

  1. BasicMessageChannel: used to pass strings and semi-structured data;
  2. MethodChannel: used to pass method calls;
  3. EventChannel: for data flow communication;

Official big picture:

Second, MethodChannel implementation principle

  • Analyzing the implementation principle of MethodChannel from the android side, it is most appropriate to select the entry where MethodChannel is used.

2.1. How to use MethodChannel

From the Android side's perspective:

//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, source code analysis

2.2.1. Constructor of MethodChannel

    public MethodChannel(BinaryMessenger messenger, String name) {
        this(messenger, name, StandardMethodCodec.INSTANCE);
    }
  • The input parameter is a communication BinaryMessenger, a binary asynchronous message interface dedicated to communicating with Flutter;
  • When using it, a FlutterView is usually passed in, because FlutterView implements this interface.

2.2.2, Android to Flutter message sending process

    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));
    }
  • In the end, the BinaryMessenger implementation class passed in by the constructor is used to send messages. There are three messages sent.

    (1) The name of the channel (2) The method name plus parameters are encoded into a bytebuff (3) The method calls back IncomingResultHandler()

Send method of 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);
    }
复制代码
  • Called the sending method of the variable mNativeView, mNativeView is a FlutterNativeView, continue to follow up

Send method of 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);
    }
复制代码
  • It gets a BinaryMessager from dartExecutor and calls its send method.
//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;
  }
复制代码
  • Obviously, the send method of BinaryMessenger is called, take a look at DefaultBinaryMessenger

The sending method of 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 is a package of DartMessenger, which does nothing.

The sending process of 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);
    }
  }
  • If there is a method to call back the callback, a replyId will be generated, and then the method callback and the replyId will be added to a map;
  • If not, send a message directly.
  • If the message body is empty, send an empty message;
  • If there is a message content, send the message in the past. It will also carry a replyId, which will be useful in callbacks.

FlutterJni distribution message

 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
  );
  • Finally, it was transferred to the c++ level, and the message was passed to flutter through the c++ dart virtual machine.

2.2.3, flutter delivers messages to native

It has been determined that the message is sent to flutter through the virtual machine at the c++ level, so the message of flutter must also be sent from the virtual machine at the c++ level, and the c++ virtual machine is the bridge between native and flutter communication.

The communication between android and c++ must be through the FlutterJni class.

What does the FlutterJni layer do

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

  private native long nativeAttach(@NonNull FlutterJNI flutterJNI, boolean isBackgroundView);
  • Pass the java class FlutterJNI into jni
  • In order to call back a method in a java class
//处理从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);
    }
  }
  • Finally mobilized the distribution method of platformMessageHandler;

PlatformMessageHandler

//通过这个方法绑定了一个方法的回调;
 public void setPlatformMessageHandler(@Nullable PlatformMessageHandler platformMessageHandler) {
    ensureRunningOnMainThread();
    this.platformMessageHandler = platformMessageHandler;
  }
  • PlatformMessageHandler is an interface that handles messages called from the Dart layer
  • DartMessenger is his implementation class, let's take a look at some methods of 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);
  }
  • In the constructor, the object of DartMessager is constructed, and then it is registered to the FlutterJNI class when onAttachToJNI is mobilized.
  • So the processing of the method is in the DartMessager class

Branch 1. Processing the callback information of flutter in DartMessager

  @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);
      }
    }
  }

First look at the method below, which is the reply after the native call to flutter. There is a replayId inside, and a reply byte array.

  1. First, find the callback saved when the message was just sent according to the replyid,
  2. If found, convert reply to bytebuff and call it out.
  3. The implementation class of BinaryReply is IncomingResultHandler

Analysis of IncomingResultHandler in MethodChannel

 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 wraps Result, and the final message is decoded in IncomingResultHandler#reply and passed to the callback.
  • This is how we call a method natively and get the result.
  • If it is a message actively mobilized from flutter, it is another line. In the DartMessage#handleMessageFromDart method, flutter active messages are processed.

Branch 2. Actively calling native information for processing flutter in DartMessager

@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);
    }
  }
  • Final callback to IncomingMethodCallHandler in MethodChannel

2.2.4. When was the handler registered?

  • From the registration in 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);
    }
  }
  • Finally, it came to DartExecutor. From the above analysis, we can know that dartExecutor.getBinaryMessenger() obtains a DefaultBinaryMessenger;
  • And DefaultBinaryMessenger holds DartMessager.
  • So setMessageHandler ends up being set into DartMessage.
  • So after the message comes from the C++ level, it will be called back to the MethodChannel;
  • What is set in MethodChannel is a MethodCallHandler, which is finally wrapped as IncomingMethodCallHandler that implements BinaryMessageHandler
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. Analysis from the perspective of Dart

Explain with a picture, do not do source code analysis

3. Timing diagram

Start by calling native from Flutter 

Return Flutter data natively

Refer to the two pictures of the big guy

Guess you like

Origin blog.csdn.net/jdsjlzx/article/details/123506356