Flutter是Google使用Dart语言开发的一套移动应用开发框架。它不同于其他开发框架:
因为Flutter使用AOT预编译代码为机器码,所以它的运行效率更高。
Flutter的UI控件并没有使用底层的原生控件,而是使用Skia渲染引擎绘制而成,因为不依赖底层控件,所以多端一致性非常好。
Flutter的扩展性也非常强,开发者可以通过Plugin与Native进行通信。
参考深入理解Flutter Platform Channel
Platform Channel 主要用于Flutter和原生端的数据传递。
Flutter定义了三种不同类型的Channel,分别是
- BasicMessageChannel:用于传递字符串和半结构化的数据;
- MethodChannel:用于传递方法调用;
- EventChannel:用于数据流的通信;
消息使用平台通道在客户端(UI)和宿主(平台)之间传递,如下图所示:
根据架构图,我们可以看出在Flutter端,MethodChannel 允许发送与方法调用相对应的消息。Android上的MethodChannel 启用接收方法调用并发回结果给Flutter端。而这种数据传递方式还可以反向调用。为了保证用户界面保持相应而不卡顿,消息和响应以异步的形式进行传递。
以确保用户界面能够保持响应。
Flutter 是通过 Dart 异步发送消息的。即便如此,当你调用一个平台方法时,也需要在主线程上做调用。
具体使用方法请参考Demo实现了一个从Flutter端发起的方法调用,从原生端获取数据并返回给Flutter端用于展示。以下是基本使用方法。
//flutter 代码
class MethodChannelManager {
MethodChannel _methodChannel;
static MethodChannelManager _instance;
String _methodChannelName = "flutter_method_channel";
MethodChannelManager._internal() {
if (_methodChannel == null) {
_methodChannel = new MethodChannel(_methodChannelName);
}
}
static MethodChannelManager getInstance() {
if (_instance == null) {
_instance = new MethodChannelManager._internal();
}
return _instance;
}
Future sendMessage() async {
Map map = {"flutter": "这是一条来自flutter的参数"};
String message = await _methodChannel.invokeMethod("success", map);
return message;
}
Future getRefreshRate() async {
return MethodChannel('fps_plugin').invokeMethod("getRefreshRate");
}
}
//android 原生端代码
public class NativeBasePlugin implements MethodCallHandler, StreamHandler, FlutterPlugin , ActivityAware {
private Context applicationContext;
private MethodChannel methodChannel;
private EventChannel eventChannel;
public static String methodChannelName = "flutter_method_channel";
public static String eventChannelName = "flutter_event_channel";
public int index = 0;
/**
* Plugin registration.
*/
public static void registerWith(FlutterEngine flutterEngine) {
final NativeBasePlugin instance = new NativeBasePlugin();
instance.onAttachedToEngine(new ShimPluginRegistry(flutterEngine).registrarFor(methodChannelName).context(),
flutterEngine.getDartExecutor());
}
@Override
public void onAttachedToEngine(FlutterPluginBinding binding) {
onAttachedToEngine(binding.getApplicationContext(), binding.getBinaryMessenger());
}
private void onAttachedToEngine(Context applicationContext, BinaryMessenger messenger) {
this.applicationContext = applicationContext;
methodChannel = new MethodChannel(messenger, methodChannelName);
methodChannel.setMethodCallHandler(this);
eventChannel = new EventChannel(messenger, eventChannelName);
eventChannel.setStreamHandler(this);
}
@Override
public void onMethodCall(MethodCall methodCall, Result result) {
switch (methodCall.method) {
case "success":
//解析参数
String value = methodCall.argument("flutter");
// Toast.makeText(applicationContext, "" + value, Toast.LENGTH_SHORT).show();
result.success("A");
break;
default:
result.notImplemented();
}
}
@Override
public void onDetachedFromEngine(FlutterPluginBinding binding) {
applicationContext = null;
methodChannel.setMethodCallHandler(null);
methodChannel = null;
eventChannel.setStreamHandler(null);
eventChannel = null;
}
@Override
public void onListen(Object arguments, EventSink events) {
/**
* CountDownTimer timer = new CountDownTimer(3000, 1000)中,
* 第一个参数表示总时间,第二个参数表示间隔时间。
* 意思就是每隔一秒会回调一次方法onTick,然后1秒之后会回调onFinish方法。
*/
CountDownTimer timer = new CountDownTimer(20000, 1000) {
public void onTick(long millisUntilFinished) {
events.success("index=" + millisUntilFinished / 1000 + "秒");
}
public void onFinish() {
events.success("时间结束了");
}
};
timer.start();
}
@Override
public void onCancel(Object arguments) {
//EventChannel 取消
index = 0;
}
}
Dart层方法调用的消息传递分析
首先,dart中会先创建一个MethodChannel对象,其名称为flutter_method_channel”,这个名字很关键,必须与原生端的名字相对应,具体原因后边会有解释。通过异步方式调用invokeMethod方法传入方法名来获取信息 await _methodChannel.invokeMethod('success');
invokeMethod方法具体实现如下
@optionalTypeArgs
Future invokeMethod(String method, [ dynamic arguments ]) async {
assert(method != null);
final ByteData result = await binaryMessenger.send(
name,
codec.encodeMethodCall(MethodCall(method, arguments)),
);
if (result == null) {
throw MissingPluginException('No implementation found for method $method on channel $name');
}
final T typedResult = codec.decodeEnvelope(result);
return typedResult;
}
通过BinaryMessages.send()方法来发送方法调用消息,我们可以看到send方法有两个参数,第一个是channel的名称,第二个是ByteData对象(使用codec对根据方法名和参数构建的MethodCall对象进行编码得到的对象);codec对象是在MethodChannel对象创建时默认创建的StandardMethodCodec对象,其对MethodCall对象的编码过程如下
const MethodChannel(this.name, [this.codec = const StandardMethodCodec(), BinaryMessenger binaryMessenger ])
: assert(name != null),
assert(codec != null),
_binaryMessenger = binaryMessenger;
@override
ByteData encodeMethodCall(MethodCall call) {
final WriteBuffer buffer = WriteBuffer();
messageCodec.writeValue(buffer, call.method);
messageCodec.writeValue(buffer, call.arguments);
return buffer.done();
}
通过messageCodec将调用的方法名和传递的参数写入到buffer中,messageCodec是一个StandardMessageCodec对象,在StandardMethodCodec对象创建时默认创建,其writeValue方法的实现如下
void writeValue(WriteBuffer buffer, dynamic value) {
if (value == null) {
buffer.putUint8(_valueNull);
} else if (value is bool) {
buffer.putUint8(value ? _valueTrue : _valueFalse);
} else if (value is double) { // Double precedes int because in JS everything is a double.
// Therefore in JS, both `is int` and `is double` always
// return `true`. If we check int first, we'll end up treating
// all numbers as ints and attempt the int32/int64 conversion,
// which is wrong. This precedence rule is irrelevant when
// decoding because we use tags to detect the type of value.
buffer.putUint8(_valueFloat64);
buffer.putFloat64(value);
} else if (value is int) {
if (-0x7fffffff - 1 <= value && value <= 0x7fffffff) {
buffer.putUint8(_valueInt32);
buffer.putInt32(value);
} else {
buffer.putUint8(_valueInt64);
buffer.putInt64(value);
}
} else if (value is String) {
buffer.putUint8(_valueString);
final List bytes = utf8.encoder.convert(value);
writeSize(buffer, bytes.length);
buffer.putUint8List(bytes);
} else if (value is Uint8List) {
buffer.putUint8(_valueUint8List);
writeSize(buffer, value.length);
buffer.putUint8List(value);
} else if (value is Int32List) {
buffer.putUint8(_valueInt32List);
writeSize(buffer, value.length);
buffer.putInt32List(value);
} else if (value is Int64List) {
buffer.putUint8(_valueInt64List);
writeSize(buffer, value.length);
buffer.putInt64List(value);
} else if (value is Float64List) {
buffer.putUint8(_valueFloat64List);
writeSize(buffer, value.length);
buffer.putFloat64List(value);
} else if (value is List) {
buffer.putUint8(_valueList);
writeSize(buffer, value.length);
for (final dynamic item in value) {
writeValue(buffer, item);
}
} else if (value is Map) {
buffer.putUint8(_valueMap);
writeSize(buffer, value.length);
value.forEach((dynamic key, dynamic value) {
writeValue(buffer, key);
writeValue(buffer, value);
});
} else {
throw ArgumentError.value(value);
}
}
上述代码看出,Flutter与平台端的消息传递支持12种类型,这12种类型分别与安卓和iOS中的类型相对应,看下面表格更直观
writeValue方法其实就是将方法名和参数转化为对应的二进制数据写入buffer中,方法名都是String类型,我们就以String类型方法写入过程来进行简单说明,如果判断一个value为String后,
1、调用buffer.putUint8(_valueString);先写入对应的类型值,_valueString = 7;,所以将00000111二进制数据写入buffer;
2、紧接着将value通过utf8编码为int数组,然后将数组的长度数据通过writeSize(buffer, bytes.length);写入buffer;
3、最后再将数组数据写入buffer,至此一个方法名编码完成;
其他类型的数据依次类推进行编码,编码完成后,将StandardMessageCodec对象编码的ByteData数据通过BinaryMessages.send()方法发送出去,看下send方法的具体实现
@override
Future send(String channel, ByteData message) {
final MessageHandler handler = _mockHandlers[channel];
if (handler != null)
return handler(message);
return _sendPlatformMessage(channel, message);
}
会从_mockHandlers中查找是否缓存的有_MessageHandler对象,如果没有则通过_sendPlatformMessage方法发送消息,
消息处理器:Handler
当我们接收二进制格式消息并使用Codec将其解码为Handler能处理的消息后,就该Handler上场了。Flutter定义了三种类型的Handler,与Channel类型一一对应。我们向Channel注册一个Handler时,实际上就是向BinaryMessager注册一个与之对应的BinaryMessageHandler。当消息派分到BinaryMessageHandler后,Channel会通过Codec将消息解码,并传递给Handler处理。
- MessageHandler
MessageHandler用户处理字符串或者半结构化的消息,其onMessage方法接收一个T类型的消息,并异步返回一个相同类型result。MessageHandler的功能比较基础,使用场景较少,但是其配合BinaryCodec使用时,能够方便传递二进制数据消息。
- MethodHandler
MethodHandler用于处理方法的调用,其onMessage方法接收一个MethodCall类型消息,并根据MethodCall的成员变量method去调用对应的API,当处理完成后,根据方法调用成功或失败,返回对应的结果。
- StreamHandler
StreamHandler与前两者稍显不同,用于事件流的通信,最为常见的用途就是Platform端向Flutter端发送事件消息。当我们实现一个StreamHandler时,需要实现其onListen和onCancel方法。而在onListen方法的入参中,有一个EventSink(其在Android是一个对象)。我们持有EventSink后,即可通过EventSink向Flutter端发送事件消息。
下面再来看下_sendPlatformMessage方法,其最终调用的是ui.window.sendPlatformMessage方法,该方法中会传递回调方法对象,在数据返回后会被回调从而得到结果数据。
void sendPlatformMessage(String name,
ByteData data,
PlatformMessageResponseCallback callback) {
final String error =
_sendPlatformMessage(name, _zonedPlatformMessageResponseCallback(callback), data);
if (error != null)
throw Exception(error);
}
String _sendPlatformMessage(String name,
PlatformMessageResponseCallback callback,
ByteData data) native 'Window_sendPlatformMessage';
在以上代码中ui.window.sendPlatformMessage()方法最终会调用Dart本地接口方法_sendPlatformMessage,这里可以将这个方法简单理解为类似于java的JNI的方法,在c++层会调用"Window_sendPlatformMessage"对应的方法。至此,dart中的方法消息传递已经结束,
c++层是如何对方法调用消息进行传递的。这里不做具体分析,有兴趣可以从Flutter engine源码中分析
接下来我们开始分析java层接受到消息后的处理逻辑。
c++层通过调用flutterJNI的handlePlatformMessage方法将channel名称、消息内容和响应ID传给java层,我们来看一下FlutterJNI中的方法实现
private void handlePlatformMessage(@NonNull String channel, byte[] message, int replyId) {
if (this.platformMessageHandler != null) {
this.platformMessageHandler.handleMessageFromDart(channel, message, replyId);
}
}
此时会调用platformMessageHandler的handleMessageFromDart()方法,handleMessageFromDart 是PlatformMessageHandler 接口里面的一个方法 在DartMessenger 里面实现
public void handleMessageFromDart(@NonNull String channel, @Nullable byte[] message, int replyId) {
Log.v("DartMessenger", "Received message from Dart over channel '" + channel + "'");
//首先根据channel名称从mMessageHandlers中查找对应的BinaryMessageHandler对象
BinaryMessageHandler handler = (BinaryMessageHandler)this.messageHandlers.get(channel);
if (handler != null) {
try {
Log.v("DartMessenger", "Deferring to registered handler to process message.");
ByteBuffer buffer = message == null ? null : ByteBuffer.wrap(message);
//如果找到则执行该对象的onMessage()方法,
handler.onMessage(buffer, new DartMessenger.Reply(this.flutterJNI, replyId));
} catch (Exception var6) {
Log.e("DartMessenger", "Uncaught exception in binary message listener", var6);
this.flutterJNI.invokePlatformMessageEmptyResponseCallback(replyId);
}
} else {
Log.v("DartMessenger", "No registered handler for message. Responding to Dart with empty reply message.");
this.flutterJNI.invokePlatformMessageEmptyResponseCallback(replyId);
}
}
我们主要看 mMessageHandlers中是怎么保存我们的channel名称为flutter_method_channel 的对象的呢,我们看下开始所说的demo中的java模块相关代码,
public static String methodChannelName = "flutter_method_channel";
methodChannel.setMethodCallHandler(this);
@Override
public void onMethodCall(MethodCall methodCall, Result result) {
switch (methodCall.method) {
case "success":
//解析参数
String value = methodCall.argument("flutter");
// Toast.makeText(applicationContext, "" + value, Toast.LENGTH_SHORT).show();
result.success("A");
break;
default:
result.notImplemented();
}
}
创建一个名为flutter_method_channel 的MethodChannel对象,然后设置对应的MethodCallHandler对象,setMethodCallHandler的方法实现如下
@UiThread
public void setMethodCallHandler(@Nullable MethodChannel.MethodCallHandler handler) {
this.messenger.setMessageHandler(this.name, handler == null ? null : new MethodChannel.IncomingMethodCallHandler(handler));
}
其中的messenger就是实现了BinaryMessenger接口的对象 再来看 this.messenger.setMessageHandler ,setMessageHandler 是一个接口,主要看他在DartMessenger 的实现类
public void setMessageHandler(@NonNull String channel, @Nullable BinaryMessageHandler handler) {
if (handler == null) {
Log.v("DartMessenger", "Removing handler for channel '" + channel + "'");
this.messageHandlers.remove(channel);
} else {
Log.v("DartMessenger", "Setting handler for channel '" + channel + "'");
this.messageHandlers.put(channel, handler);
}
}
到此,我们发现在注册插件方法中实现的 MethodCallHandler通过一系列操作被包装到MethodChannel.IncomingMethodCallHandler对象中并设置进了mMessageHandlers中。那么我们上面所说的onMessage方法的调用即是MethodChannel.IncomingMethodCallHandler对象的方法,
private final class IncomingMethodCallHandler implements BinaryMessageHandler {
private final MethodChannel.MethodCallHandler handler;
IncomingMethodCallHandler(MethodChannel.MethodCallHandler handler) {
this.handler = handler;
}
@UiThread
public void onMessage(ByteBuffer message, final BinaryReply reply) {
//首先将从c++层传递过来的消息通过codec解码为MethodCall对象
MethodCall call = MethodChannel.this.codec.decodeMethodCall(message);
try {
this.handler.onMethodCall(call, new MethodChannel.Result() {
public void success(Object result) {
reply.reply(MethodChannel.this.codec.encodeSuccessEnvelope(result));
}
public void error(String errorCode, String errorMessage, Object errorDetails) {
reply.reply(MethodChannel.this.codec.encodeErrorEnvelope(errorCode, errorMessage, errorDetails));
}
public void notImplemented() {
reply.reply((ByteBuffer)null);
}
});
} catch (RuntimeException var5) {
Log.e("MethodChannel#" + MethodChannel.this.name, "Failed to handle method call", var5);
reply.reply(MethodChannel.this.codec.encodeErrorEnvelope("error", var5.getMessage(), (Object)null));
}
}
}
方法中首先将从c++层传递过来的消息通过codec解码为MethodCall对象,然后调用注册的地方实现的MethodHandler的onMethodCall方法,该方法会接受flutter 传递的参数,
case "success":
//解析参数
String value = methodCall.argument("flutter");
// Toast.makeText(applicationContext, "" + value, Toast.LENGTH_SHORT).show();
result.success("A");
break;
然后调用result.success()方法,通过reply.reply(MethodChannel.this.codec.encodeSuccessEnvelope(result));将结果数据编码后进行返回。reply方法中会调用
DartMessenger .this.mFlutterJNI.invokePlatformMessageResponseCallback(replyId, reply, reply.position());方法将响应结果返回,方法具体实现如下
public void reply(@Nullable ByteBuffer reply) {
if (this.done.getAndSet(true)) {
throw new IllegalStateException("Reply already submitted");
} else {
if (reply == null) {
this.flutterJNI.invokePlatformMessageEmptyResponseCallback(this.replyId);
} else {
this.flutterJNI.invokePlatformMessageResponseCallback(this.replyId, reply, reply.position());
}
}
}
最终会调用JNI方法将数据返回给c++层,
Dart层接收消息响应后的处理流程分析
通过以上Dart层传递消息分析可知PlatformMessageResponseCallback方法回调后对byte_buffer数据进行处理,通过completer.complete()方法完成返回数据,然后一步步返回到调用方法层,在异步方法中通过await等待数据返回后,再通过setState改变State中的变量值从而刷新页面数据信息显示到屏幕上。至此,整个flutter发消息给platform并接收消息处理的流程就完成了。
简约调用关系图如下
总结,整个消息发送和接收结果的流程分为以下几步:
- Dart层通过以上提到的12种类型包含的类型数据进行编码,然后通过dart的类似jni的本地接口方法传递给c++层;
- c++层通过持有java对象flutterJNI的方法调用将消息传递到java层;
- java层解码接收到的消息,根据消息内容做指定的逻辑处理,得到结果后进行编码通过jni方法将响应结果返回给c++层;
- c++层处理返回的响应结果,并将结果通过发送时保存的dart响应方法对象回调给Dart层;
Dart层通过回调方法对结果数据进行处理,然后通过codec解码数据做后续的操作;