@TryLoveCatch
2019-08-28T11:18:08.000000Z
字数 16882
阅读 2315
flutter
Flutter如何和Native通信-Android视角
深入理解Flutter Platform Channel
底层原理 - 深入探索Platform Channel
Flutter Platform Channel 使用与源码分析
Flutter是可以同时在iOS和Android系统上运行的,那么就有一个问题,Flutter如何和Native进行通信呢?。比如说,Flutter app要显示手机的电量,而电量只能通过平台的系统Api获取。这时就需要有个机制使得Flutter可以通过某种方式来调用这个系统Api并且获得返回值。那么Flutter是如何做到的呢?答案是Platform Channels

通过Platform Channels传递的消息都是异步的
Flutter定义了三种不同类型的Channel,它们分别是:
channel的名字,可以理解为id,就是独一无二的存在,flutter和native保持一致即可。
消息信使,BinaryMessenger类型,BinaryMessenger是PlatformChannel与Flutter端的通信的工具,其通信使用的消息格式为二进制格式数据,BinaryMessenger在Android中是一个接口,它的实现类为FlutterNativeView。
Codec是消息编解码器,主要用于将二进制格式的数据转化为Handler能够识别的数据,Flutter定义了两种Codec:MessageCodec和MethodCodec。MessageCodec用于二进制格式数据与基础数据之间的编解码,BasicMessageChannel所使用的编解码器是MessageCodec。MethodChannel和EventChannel所使用的编解码均为MethodCodec。
Flutter定义了三种类型的Handler,它们与PlatformChannel类型一一对应,分别是MessageHandler、MethodHandler、StreamHandler。在使用PlatformChannel时,会为它注册一个Handler,PlatformChannel会将该二进制数据通过Codec解码为转化为Handler能够识别的数据,并交给Handler处理。当Handler处理完消息之后,会通过回调函数返回result,将result通过编解码器编码为二进制格式数据,通过BinaryMessenger发送回Flutter端。
public interface BinaryMessenger {void send(String channel, ByteBuffer message);void send(String channel, ByteBuffer message, BinaryReply callback);void setMessageHandler(String channel, BinaryMessageHandler handler);interface BinaryMessageHandler {void onMessage(ByteBuffer message, BinaryReply reply);}interface BinaryReply {void reply(ByteBuffer reply);}}
消息编解码器Codec主要用于将二进制格式的数据转化为Handler能够识别的数据,Flutter定义了两种Codec:MessageCodec和MethodCodec。
MessageCodec用于二进制格式数据与基础数据之间的编解码。BasicMessageChannel所使用的编解码器就是MessageCodec。
Android中,MessageCodec是一个接口,定义了两个方法:
public interface MessageCodec<T> {ByteBuffer encodeMessage(T message);T decodeMessage(ByteBuffer message);}
MessageCodec有多个不同的实现:
原封不动的返回二进制数据,可以使传递内存数据块时在编解码阶段免于内存拷贝。Android中为ByteBuffer
字符串与二进制数据之间的编解码,其编码格式为UTF-8
基础数据与二进制数据之间的编解码,其支持基础数据类型以及列表、字典。
BasicMessageChannel的默认编解码器,其支持基础数据类型、二进制数据、列表、字典。
MethodCodec用于二进制数据与方法调用(MethodCall)和返回结果之间的编解码。MethodChannel和EventChannel所使用的编解码器均为MethodCodec。
MethodCodec用于MethodCall对象的编解码,一个MethodCall对象代表一次从Flutter端发起的方法调用。
public interface MethodCodec {Object decodeEnvelope(ByteBuffer envelope)MethodCall decodeMethodCall(ByteBuffer methodCall)ByteBuffer encodeErrorEnvelope(String errorCode, String errorMessage, Object errorDetails)ByteBuffer encodeMethodCall(MethodCall methodCall)ByteBuffer encodeSuccessEnvelope(Object result)}
MethodCall有2个成员变量:
public interface MethodCall {Object argumentsString method}
由于处理的是方法调用,故相比于MessageCodec,MethodCodec多了对调用结果的处理。
MethodCodec有两种实现:
JSONMethodCodec的编解码依赖于JSONMessageCodec,当其在编码MethodCall时,会先将MethodCall转化为字典{"method":method,"args":args}。其在编码调用结果时,会将其转化为一个数组,调用成功为[result],调用失败为[code,message,detail]。再使用JSONMessageCodec将字典或数组转化为二进制数据。
比如当前想要调用某个Channel的setVolum(5),其对应的MethodCall被被转成{"method":"setVolum","args":{"volum":5}}
MethodCodec的默认实现,StandardMethodCodec的编解码依赖于StandardMessageCodec,当其编码MethodCall时,会将method和args依次使用StandardMessageCodec编码,写入二进制数据容器。其在编码方法的调用结果时,若调用成功,会先向二进制数据容器写入数值0(代表调用成功),再写入StandardMessageCodec编码后的result。而调用失败,则先向容器写入数据1(代表调用失败),再依次写入StandardMessageCodec编码后的code,message和detail。
| - | Codec | Desc |
|---|---|---|
| MessageCodec | StandardMessageCodec | 支持基础数据格式与二进制之间的转换,包括 Boolean 、Number、String、Byte、List、Map、数组等。 |
| BinaryCodec | 支持二进制数据的直接传递,实际上是没有做任何编解码,可以通过它做大内存块的数据传递。 | |
| StringCodec | 支持字符串(UTF-8)与二进制之间的转换。 | |
| JSONMessageCodec | 支持把数据转换为JSON,再调用 StringCodec 的编解码。 | |
| MethodCodec | StandardMethodCodec | 调用 StandardMessageCodec 传递方法的名字和参数。 |
| JSONMethodCodec | 调用 JSONMessageCodec 传递方法的名字和参数。 |
当我们接收二进制格式消息并使用Codec将其解码为Handler能处理的消息后,就该Handler上场了。
Flutter定义了三种类型的Handler,与Channel类型一一对应。
我们向Channel注册一个Handler时,实际上就是向BinaryMessager注册一个与之对应的BinaryMessageHandler。当消息派分到BinaryMessageHandler后,Channel会通过Codec将消息解码,并传递给Handler处理。
MessageHandler用户处理字符串或者半结构化的消息,其onMessage方法接收一个T类型的消息,并异步返回一个相同类型result。
public static interface BasicMessageChannel.MessageHandler<T> {void onMessage(T message, BasicMessageChannel.Reply<T> reply)}
MethodHandler用于处理方法的调用,其onMessageCall方法接收一个MethodCall类型消息,并根据MethodCall的成员变量method去调用对应的API,当处理完成后,根据方法调用成功或失败,通过Result返回对应的结果。
public static interface MethodChannel.MethodCallHandler {void onMethodCall(MethodCall call, MethodChannel.Result result)}
StreamHandler与前两者稍显不同,用于事件流的通信,最为常见的用途就是Platform端向Flutter端发送事件消息。当我们实现一个StreamHandler时,需要实现其onListen和onCancel方法。而在onListen方法的入参中,有一个EventSink(其在Android是一个对象,iOS端则是一个block)。我们持有EventSink后,即可通过EventSink向Flutter端发送事件消息。
public static interface EventChannel.StreamHandler {void onCancel(Object arguments)void onListen(Object arguments, EventChannel.EventSink events)}public static interface EventChannel.EventSink {void endOfStream()void error(String errorCode, String errorMessage, Object errorDetails)void success(Object event)}
| -- | name | messager | codec | handler |
|---|---|---|---|---|
| BasicMessageChannel | MessageCodec-StandardMessageCodec | MessageHandler | ||
| MethodChannel | MethodCodec-StandardMethodCodec | MethodHandler | ||
| EventChannel | MethodCodec-StandardMethodCodec | StreamHandler |
Dart代码:
第一步static const methodChannel = MethodChannel("method_channel_sample");第二步Future<dynamic> getUserInfo(String method, {String userName}) async {return await methodChannel.invokeMethod(method, userName);}第三步MaterialButton(color: Colors.blue,textColor: Colors.white,child: new Text('获取 snow 用户信息'),onPressed: () {getUserInfo("getInfo", userName: "snow")..then((result) {setState(() {messageFromNative = result;});});},),
Android代码:
private void addMethodChannel() {mMethodChannel = new MethodChannel(getFlutterView(), "method_channel_sample");mMethodChannel.setMethodCallHandler((methodCall, result) -> {String method = methodCall.method;if ("getInfo".equals(method)) {String userName = (String) methodCall.arguments;if (userName.equals("rocx")) {String user = "name:rocx, age:18";result.success(user);} else {result.success("user not found");invokeSayHelloMethod();}}});}
Dart 调用 Android 代码分三步。
- 首先在 Dart 端定义 MethodChannel 名字为 method_channel_sample。
- 然后定义getUserInfo方法,传入要调用的方法名和参数。
- 最后点击按钮执行方法,获取用户信息。
在 Android 端定一个 MethodChannel 名字和 Dart 端保持一致。设置 MethodCallHandler。当调用的是getInfo方法时,根据参数返回信息。
Android代码:
private void invokeSayHelloMethod() {mMethodChannel.invokeMethod("sayHello", "", new MethodChannel.Result() {@Overridepublic void success(Object o) {Toast.makeText(MainActivity.this, o.toString(), Toast.LENGTH_LONG).show();}@Overridepublic void error(String s, String s1, Object o) {}@Overridepublic void notImplemented() {}});}
dart端代码:
Future<dynamic> addHandler(MethodCall call) async {switch (call.method) {case "sayHello":return "Hello from Flutter";break;}}@overridevoid initState() {super.initState();methodChannel.setMethodCallHandler(addHandler);}
在 Dart 端设置 MethodCallHandler 然后在 Android 端调用即可
dart端代码:
第一步static const basicMessageChannel = BasicMessageChannel("basic_message_channel_sample", StandardMessageCodec());第二步Future<dynamic> sayHelloToNative(String message) async {String reply = await basicMessageChannel.send(message);setState(() {msgReplyFromNative = reply;});return reply;}第三步MaterialButton(color: Colors.blue,textColor: Colors.white,child: new Text('say hello to native'),onPressed: () {sayHelloToNative("hello");},),
Android端代码:
private void addBasicMessageChannel() {mBasicMessageChannel = new BasicMessageChannel<>(getFlutterView(), "basic_message_channel_sample", StandardMessageCodec.INSTANCE);mBasicMessageChannel.setMessageHandler((object, reply) -> {reply.reply("receive " + object.toString() + " from flutter");mBasicMessageChannel.send("native say hello to flutter");});}
Dart 向 Android 发送消息依然分为三步。
- 首先在 Dart 端定义 BasicMessageChannel 名字为 basic_message_channel_sample。
- 然后定义发送消息的方法sayHelloToNative。
- 最后点击按钮向 Android 端发送消息。
在 Android 端定一个 BasicMessageChannel 名字和 Dart 端保持一致。设置 MethodCallHandler。当收到消息时发一个回复。
Android端代码:
mBasicMessageChannel.send("native say hello to flutter");
dart端代码:
Future<dynamic> addHandler(Object result) async {setState(() {msgReceiveFromNative = result.toString();});}void addMessageListener() {basicMessageChannel.setMessageHandler(addHandler);}@overridevoid initState() {super.initState();addMessageListener();}
在 Dart 端设置 MessageHandler 然后在 Android 端直接发送消息即可。
dart端代码:
第一步static const eventChannel = EventChannel("event_channel_sample");void _onEvent(Object event) {setState(() {if (_streamSubscription != null) {eventMessage = event.toString();}});}void _onError(Object error) {setState(() {if (_streamSubscription != null) {eventMessage = "error";}});}@overridevoid initState() {super.initState();eventMessage = "";第二步_streamSubscription = eventChannel.receiveBroadcastStream().listen(_onEvent, onError: _onError);}
Android端代码:
private void addEventChannel() {mEventChannel = new EventChannel(getFlutterView(), "event_channel_sample");mEventChannel.setStreamHandler(new EventChannel.StreamHandler() {@Overridepublic void onListen(Object o, EventChannel.EventSink eventSink) {task = new TimerTask() {@Overridepublic void run() {runOnUiThread(() -> eventSink.success("i miss you " + System.currentTimeMillis()));}};timer = new Timer();timer.schedule(task, 2000, 3000);}@Overridepublic void onCancel(Object o) {task.cancel();timer.cancel();task = null;timer = null;}});}
Dart 接受 Android stream event。
- 首先在 Dart 端定义 EventChannel 名字为 event_channel_sample。
- 然后设置receiveBroadcastStream监听,当 Android 端有消息发过来会回调_onEvent方法。
在 Android 端启动一个定时器,每隔3s向 Dart 端发送一次消息。
如下图,在 Dart 与 Platform 通信过程中,通过 channel name 找到对方,然后把消息通过 codec 进行编解码,最后通过 binaryMessenger 进行发送。

我们来说一下流程:
- 当我们初始化一个Channel,也就是new的时候,会传入name,这个name就是这个channel的唯一标示。
- 当我们向该Channel注册处理消息的Handler时,实际上会生成一个BinaryMessageHandler,并以channel name为key,注册到BinaryMessenger中。也就是说一个channel,有一个唯一标示的name,有一个与之对应的BinaryMessageHandler。
- 当Flutter端发送消息到BinaryMessenger时,BinaryMessenger会根据其入参channel找到与之对应的BinaryMessageHandler,并交由其处理。
- Binarymessenger并不知道Channel的存在,它只和BinaryMessageHandler打交道。而Channel和BinaryMessageHandler则是一一对应的。
- 由于Channel从BinaryMessageHandler接收到的消息是二进制格式数据,无法直接使用,故Channel会将该二进制消息通过Codec(消息编解码器)解码为能识别的消息并传递给Handler进行处理。
- 当Handler处理完消息之后,会通过回调函数返回result,并将result通过编解码器编码为二进制格式数据,通过BinaryMessenger发送回Flutter端。
有没有觉得根Binder很像?我们来简化一下:
Android侧:
<name, BinaryMessageHandler>Futter侧:
Android侧:
我们以MethodChannel为例,先来看使用:
// Dart层static const methodChannel = MethodChannel("method_channel_sample");Future<dynamic> getUserInfo() async {return await methodChannel.invokeMethod("getInfo", {userName: "snow"});}=======================================================// Andrid层private void addMethodChannel() {mMethodChannel = new MethodChannel(getFlutterView(), "method_channel_sample");mMethodChannel.setMethodCallHandler((methodCall, result) -> {String method = methodCall.method;if ("getInfo".equals(method)) {String userName = (String) methodCall.arguments;if (userName.equals("rocx")) {String user = "name:rocx, age:18";result.success(user);} else {result.success("user not found");invokeSayHelloMethod();}}});}
这是一个简单的Dart调用Android的
class MethodChannel {// 构造方法,通常我们只需要指定该channel的name,const MethodChannel(this.name, [this.codec = const StandardMethodCodec()]);// name作为通道的唯一标志付,用于区分不同的通道调用final String name;// 用于方法调用过程的编码final MethodCodec codec;// 用于发起异步平台方法调用,需要指定方法名,以及可选方法参数Future<dynamic> invokeMethod(String method, [dynamic arguments]) async {assert(method != null);// 将一次方法调用中需要的方法名和方法参数封装为MethodCall对象,然后使用MethodCodec对该// 对象进行进行编码操作,最后通过BinaryMessages中的send方法发起调用final dynamic result = await BinaryMessages.send(name,codec.encodeMethodCall(MethodCall(method, arguments)),);if (result == null)throw MissingPluginException('No implementation found for method $method on channel $name');return codec.decodeEnvelope(result);}}
一般来说,我们会创建MethodChannel对后,调用其invokeMethod()方法用于向平台发起一次调用
在invokeMethod()方法中会将一次方法调中的方法名method和方法参数arguments封装为MethodCall对象
然后使用MethodCodec对其进行二进制编码
最后通过Dart层的BinaryMessages的send()发起平台方法调用请求
class BinaryMessages {......static Future<ByteData> send(String channel, ByteData message) {final _MessageHandler handler = _mockHandlers[channel];// 在没有设置Mock Handler的情况下,继续调用_sendPlatformMessage()if (handler != null)return handler(message);return _sendPlatformMessage(channel, message);}static Future<ByteData> _sendPlatformMessage(String channel, ByteData message) {final Completer<ByteData> completer = Completer<ByteData>();ui.window.sendPlatformMessage(channel, message, (ByteData reply) {try {completer.complete(reply);} catch (exception, stack) {FlutterError.reportError(FlutterErrorDetails(exception: exception,stack: stack,library: 'services library',context: 'during a platform message response callback',));}});return completer.future;}......}
Dart层的BinaryMessages类中提供了用于发送和接受平台插件的二进制消息.最终会调用ui.window.sendPlatformMessage().
然后,会通过一系列的JNI方法,最后调用到Android层的BinaryMessenger里面的handleMessageFromDart(),而FlutterNativeView就是一个BinaryMessenger,所以它内部包含了一个Map,channel name和BinaryMessageHandler的对应关系
public class FlutterNativeView implements BinaryMessenger {private final Map<String, BinaryMessageHandler> mMessageHandlers;......private final class PlatformMessageHandlerImpl implements PlatformMessageHandler {// Called by native to send us a platform message.public void handleMessageFromDart(final String channel, byte[] message, final int replyId) {// 1.根据channel名称获取对应的BinaryMessageHandler对象.每个Channel对应一个// Handler对象BinaryMessageHandler handler = mMessageHandlers.get(channel);if (handler != null) {try {// 2.将字节数组对象封装为ByteBuffer对象final ByteBuffer buffer = (message == null ? null : ByteBuffer.wrap(message));// 3.调用handler对象的onMessage()方法来分发消息handler.onMessage(buffer, new BinaryReply() {private final AtomicBoolean done = new AtomicBoolean(false);@Overridepublic void reply(ByteBuffer reply) {// 4.根据reply的情况,调用FlutterJNI中invokePlatformMessageXXX()方法将响应数据发送给Flutter层if (reply == null) {mFlutterJNI.invokePlatformMessageEmptyResponseCallback(replyId);} else {mFlutterJNI.invokePlatformMessageResponseCallback(replyId, reply, reply.position());}}});} catch (Exception exception) {mFlutterJNI.invokePlatformMessageEmptyResponseCallback(replyId);}return;}mFlutterJNI.invokePlatformMessageEmptyResponseCallback(replyId);}}
首先根据Channel名称从mMessageHandlers取出对应的二进制消息处理器BinaryMessageHandler
然后将字节数组message封装为ByteBuffer对象
然后调用BinaryMessageHandler实例的onMessage()方法处理ByteBuffer,并进行响应.
我们接下来看一下BinaryMessageHandler的onMessage()方法
public final class MethodChannel {private final BinaryMessenger messenger;private final String name;private final MethodCodec codec;......public void setMethodCallHandler(final @Nullable MethodCallHandler handler) {messenger.setMessageHandler(name,handler == null ? null : new IncomingMethodCallHandler(handler));}public interface Result {void success(@Nullable Object result);void error(String errorCode, @Nullable String errorMessage, @Nullable Object errorDetails);void notImplemented();}public interface MethodCallHandler {void onMethodCall(MethodCall call, Result result);}private final class IncomingMethodCallHandler implements BinaryMessageHandler {private final MethodCallHandler handler;IncomingMethodCallHandler(MethodCallHandler handler) {this.handler = handler;}@Overridepublic void onMessage(ByteBuffer message, final BinaryReply reply) {// 1.使用codec对来自Flutter方法调用数据进行解码,并将其封装为MethodCall对象.// MethodCall中包含两部分数据:method表示要调用的方法;arguments表示方法所需参数final MethodCall call = codec.decodeMethodCall(message);try {// 2.调用自定义MethodCallHandler中的onMethodCall方法继续处理方法调用handler.onMethodCall(call, new Result() {@Overridepublic void success(Object result) {// 调用成功时,需要回传数据给Flutter层时,使用codec对回传数据result// 进行编码reply.reply(codec.encodeSuccessEnvelope(result));}@Overridepublic void error(String errorCode, String errorMessage, Object errorDetails) {// 调用失败时,需要回传错误数据给Flutter层时,使用codec对errorCode,// errorMessage,errorDetails进行编码reply.reply(codec.encodeErrorEnvelope(errorCode, errorMessage, errorDetails));}@Overridepublic void notImplemented() {// 方法没有实现时,调用该方法后,flutter将会受到相应的错误消息reply.reply(null);}});} catch (RuntimeException e) {Log.e(TAG + name, "Failed to handle method call", e);reply.reply(codec.encodeErrorEnvelope("error", e.getMessage(), null));}}}}
首先使用codec对来自Flutter层的二进制数据进行解码,并将其封装为MethodCall对象
然后调用MethodCallHandler的onMethodCall()方法.
接下来就是handler的处理,这个是我们自己的逻辑了:
mMethodChannel.setMethodCallHandler((methodCall, result) -> {String method = methodCall.method;if ("getInfo".equals(method)) {String userName = (String) methodCall.arguments;if (userName.equals("rocx")) {String user = "name:rocx, age:18";result.success(user);} else {result.success("user not found");invokeSayHelloMethod();}}});
我们会返回一个字符串给Dart
Dart层 --> MethodChannel.invokeMethod() --> codec编码MethodCall,包含方法名和参数 --> BinaryMessages.send() --> JNI --> Android层 --> BinaryMessage.handleMessageFromDart() --> BinaryMessageHandler.onMessage() 解码MethodCall,拿到方法名和参数 --> Handler.onMethodCall() --> 调用方法,返回结果 --> BinaryMessageHandler 编码结果二进制 --> BinaryMessage --> JNI --> Dart层