Flutter MethodChannel分析
Posted 一叶飘舟
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flutter MethodChannel分析相关的知识,希望对你有一定的参考价值。
一、三种Channel
Flutter中通过Platform Channel实现Flutter和原生端的数据传递,那么这些数据是怎么传递的,传递的过程都做了哪些操作
Flutter定义了三种不同类型的Channel,分别是
- BasicMessageChannel:用于传递字符串和半结构化的数据;
- MethodChannel:用于传递方法调用;
- 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字节数组。
- 首先根据replyid找到刚才发送消息时保存的callback,
- 如果找到了,就把reply转化为bytebuff调用出去。
- 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 与 Firebase onBackgroundMessage 一起使用