Flutter Packages & Plugins(二)

Flutter Packages & Plugins.

Posted by 杨小狼不吃羊 on 2020-10-28
Words 1.9k and Reading Time 8 Minutes
Viewed Times

Flutter Packages & Plugins(二)

在上一篇中我们中我们熟悉了pub、package的种类,以及从创建到发布的整个流程,本篇将总结下flutter调用其他平台原始代码的原理,尤其是Android平台的实现原理。

本篇主要内容来源于flutter官方文档和博客码字坞

1. 架构概述:平台通道

Flutter 内置的平台特定 API 支持不依赖于任何生成代码,而是灵活的依赖于传递消息格式。或者,你也可以使用 Pigeon 这个 package,通过生成代码来 发送结构化类型安全消息。

  • 应用程序中的 Flutter 部分通过平台通道向其宿主(应用程序中的 iOS 或 Android 部分)发送消息。
  • 宿主监听平台通道并接收消息。然后,它使用原生编程语言来调用任意数量的相关平台 API,并将响应发送回客户端(即应用程序中的 Flutter 部分)。

消息使用平台通道在客户端(UI)和宿主(平台)之间传递,如下图所示:

20201102221250-ZTYvcD

2. Android Platform Channel

flutter的android平台的通讯Channel大致有三种。

2.1 Channel种类

Name Function
BasicMessageChannel 用于简单的数据传递
MethodChannel 传递一个方法名和参数来完成通讯
EventChannel 类似于长连接的通讯

2.2 Channel结构

20201102225814-DsdKp4

每个Channel都会配置一个Codec,用于通讯数据的编解码。Flutter的平台通讯方式采用了二进制协议,图中的 MethodCodec和MessageCodec的实现者都是采用了字节写入写出完成数据的编解码。

MessageCodec完成基础的编解码,MethodCodec在MessageCodec的基础上进一步完成方法名字的编解码。 MessageCodec目前的实现类有四个,BinaryCodec基本不做操作,直接将获得的字节数据返回;StringCodec完成 字节和字符串的转化;JSONMessageCodec完成Json结构的字符串与字节之间转化;而StandardMessageCodec采用了 更加细分的规则。

总的来说参数传递时都是只支持基本数据类型和List与Map类型的数据,只不过StandardMessageCodec比 其他的Codec更加精准,StandardMessageCodec能根据数字编号直接确定当前读到的字节数据是何种类型。

2.3 数据结构

20201102230100-FI0C8r
如图中所示,一串数据,开头字节表示为类型type,然后才是数据本体,比如当前type表示为Int型数据,那么在取数据时 直接以int形式的方式将data取出。

而MethodCodec先将方法名取出,然后将参数取出,实际还是由MessageCodec来处理。
20201102230324-J23OKR
在Flutter android中真正完成数据收发的是DartMessenger,DartMessenger包含了两个回调集合,一个关于BinaryMessageHandler 的回调集合用于接收来自Flutter的主动调用,一个BinaryReply集合用于接收Flutter对于平台调用的响应,当然BinaryMessageHandler 是需要事先完成注册的,这样Flutter调用原生时才不会扑空。

3. 通讯流程

对通道有了一个初步的认识之后,我们继续看Flutter是如何实现平台通讯的。

3.1 注册

Flutter想要调用android平台上一个功能,android就必须实现注册一个接收器,这个接收器就是BinaryMessageHandler。android平台事先会将自己定义的Channel进行注册,这里我们选择MethodChannel。并且实现了一个FlutterPlugin 插件,继承于MethodCallHandler。 比如我注册名字就是 bridge

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class BridgePlugin : FlutterPlugin, MethodCallHandler {

private lateinit var channel: MethodChannel
private lateinit var context: Context

override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "bridge")
channel.setMethodCallHandler(this)
context = flutterPluginBinding.applicationContext
}

override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {

}

在我们的项目中,使用到的每一个原生插件都会在对应平台生成如下的代码进行注册:
20201102233001-WX20201102-225041
调用了setMethodCallHandler之后会封装一个BinaryMessageHandler。

1
2
3
4
5
6
7
8
9
10
11
public final class MethodChannel {
...
public void setMethodCallHandler(@Nullable MethodChannel.MethodCallHandler handler) {
this.messenger.setMessageHandler(this.name, handler == null ? null : new MethodChannel.IncomingMethodCallHandler(handler));
}
...
private final class IncomingMethodCallHandler implements BinaryMessageHandler {
...
}
...
}

最终会调用到DartMessenger的setMessageHandler,根据注册名字来完成注册。

1
2
3
4
5
6
7
8
9
10
11
12
13
class DartMessenger implements BinaryMessenger, PlatformMessageHandler {
...
private final Map<String, BinaryMessageHandler> messageHandlers;
...
public void setMessageHandler(@NonNull String channel, @Nullable BinaryMessageHandler handler) {
if (handler == null) {
this.messageHandlers.remove(channel);
} else {
this.messageHandlers.put(channel, handler);
}
}
...
}

3.2 调用

20201102233546-EhRG4N

完成注册之后,从Flutter发起调用。由Dart完成调用逻辑,Dart也实现了跟Java层一样的对接接口, 调用了MethodChannel来完成分发。在MethodChannel中调用codec将参数封装成二进制数据,然后调用 binaryMessenger发送。这里属于异步调用,可以直接将任务的Future返回。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Future<T> invokeMethod<T>(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;
}

在Dart层代码中binaryMessenger的实现者目前只有_DefaultBinaryMessenger,_DefaultBinaryMessenger 关于发送的逻辑的实现如下,并且设置了相应的回调。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class _DefaultBinaryMessenger extends BinaryMessenger {
...
Future<ByteData> _sendPlatformMessage(String channel, ByteData message) {
final Completer<ByteData> completer = Completer<ByteData>();
//最终调用了window的发送渠道
ui.window.sendPlatformMessage(channel, message, (ByteData reply) {
...
completer.complete(reply);
...
});
return completer.future;
}
...
@override
Future<ByteData> send(String channel, ByteData message) {
...
return _sendPlatformMessage(channel, message);
}
...
}

在window的实现中最终调用了native层的代码
1
2
3
4
//window.dart
String _sendPlatformMessage(String name,
PlatformMessageResponseCallback callback,
ByteData data) native 'Window_sendPlatformMessage';

Window_sendPlatformMessage 这个标示是在Engine层事先注册过的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//window.cc

Dart_Handle SendPlatformMessage(Dart_Handle window,
const std::string& name,
Dart_Handle callback,
const tonic::DartByteData& data) {
...
dart_state->window()->client()->HandlePlatformMessage(
fml::MakeRefCounted<PlatformMessage>(
name, std::vector<uint8_t>(buffer, buffer + data.length_in_bytes()),
response));
...
}

void _SendPlatformMessage(Dart_NativeArguments args) {
tonic::DartCallStatic(&SendPlatformMessage, args);
}
void _SendPlatformMessage(Dart_NativeArguments args) {
tonic::DartCallStatic(&SendPlatformMessage, args);
}

void Window::RegisterNatives(tonic::DartLibraryNatives* natives) {
natives->Register({
{"Window_defaultRouteName", DefaultRouteName, 1, true},
{"Window_scheduleFrame", ScheduleFrame, 1, true},
{"Window_sendPlatformMessage", _SendPlatformMessage, 4, true},
{"Window_respondToPlatformMessage", _RespondToPlatformMessage, 3, true},
{"Window_render", Render, 2, true},
{"Window_updateSemantics", UpdateSemantics, 2, true},
{"Window_setIsolateDebugName", SetIsolateDebugName, 2, true},
{"Window_reportUnhandledException", ReportUnhandledException, 2, true},
});
}

这里有个client的转发,具体的关联如下图。

20201102234035-ERAgoj

最终会分发到PlatformViewAndroid的HandlePlatformMessage上,之后就会使用JNI技术传给Java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
void PlatformViewAndroid::HandlePlatformMessage(
fml::RefPtr<blink::PlatformMessage> message) {
...
FlutterViewHandlePlatformMessage(env, view.obj(), java_channel.obj(),
nullptr, response_id);
...
}
}

void FlutterViewHandlePlatformMessage(JNIEnv* env,
jobject obj,
jstring channel,
jobject message,
jint responseId) {
env->CallVoidMethod(obj, g_handle_platform_message_method, channel, message,
responseId);
FML_CHECK(CheckException(env));
}
...
g_flutter_jni_class = new fml::jni::ScopedJavaGlobalRef<jclass>(
env, env->FindClass("io/flutter/embedding/engine/FlutterJNI"));
...
g_handle_platform_message_method =
env->GetMethodID(g_flutter_jni_class->obj(), "handlePlatformMessage",
"(Ljava/lang/String;[BI)V");
...

上边代码显示,最终java层的FlutterJNI类的handlePlatformMessage方法会被调用。
1
2
3
4
5
6
7
8
9
10
public class FlutterJNI {
...
private void handlePlatformMessage(@NonNull String channel, byte[] message, int replyId) {
if (this.platformMessageHandler != null) {
this.platformMessageHandler.handleMessageFromDart(channel, message, replyId);
}

}
...
}

继续分发,会给到platformMessageHandler调用。platformMessageHandler其实就是DartMessenger
1
2
3
4
5
6
7
8
9
10
class DartMessenger implements BinaryMessenger, PlatformMessageHandler {
public void handleMessageFromDart(@NonNull String channel, @Nullable byte[] message, int replyId) {
BinaryMessageHandler handler = (BinaryMessageHandler)this.messageHandlers.get(channel);
...
ByteBuffer buffer = message == null ? null : ByteBuffer.wrap(message);
handler.onMessage(buffer, new DartMessenger.Reply(this.flutterJNI, replyId));
...
}

}

这里会取出之前注册时 ,相应的名字对应的BinaryMessageHandler,这个BinaryMessageHandler应该时MethodChannel中实现的 IncomingMethodCallHandler,而MethodCallHandler就是插件需要实现的逻辑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public final class MethodChannel {
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) {
MethodCall call = MethodChannel.this.codec.decodeMethodCall(message);
...
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);
}
});
...
}
}
}

3.3 回传

上面就完成了调用过程,下面就是回传过程:

20201102234454-2wxAQ0


This is copyright.

...

...

00:00
00:00