消息通过平台通道在native(host)与flutter(client)之间传递,如下图所示:
为了确保用户界面能够正确响应,消息都是以异步的方式进行传递。无论是native向flutter发送消息,还是flutter向native发送消息。
在flutter中,MethodChannel可以发送与方法调用相对应的消息。在native平台上,MethodChannel在Android可以接收方法调用并返回结果。这些类可以帮助我们用很少的代码就能开发平台插件。
注意:本节内容来自flutter官网,读者可自行查阅。
平台通道可以使用提供的编解码器对消息进行编解码,这些编解码器支持简单类似JSON的值的高效二进制序列化,例如布尔值,数字,字符串,字节缓冲区以及这些的列表和映射。当你发送和接收值时,会自动对这些值进行序列化和反序列化。
下表显示了如何在平台端接收Dart值,反之亦然:
Dart | Android | iOS |
---|---|---|
null | null | nil (NSNull when nested) |
bool | java.lang.Boolean | NSNumber numberWithBool: |
int | java.lang.Integer | NSNumber numberWithInt: |
int, if 32 bits not enough | java.lang.Long | NSNumber numberWithLong: |
int, if 64 bits not enough | java.math.BigInteger | FlutterStandardBigInteger |
double | java.lang.Double | NSNumber numberWithDouble: |
String | java.lang.String | NSString |
Uint8List | byte[] | FlutterStandardTypedData typedDataWithBytes: |
Int32List | int[] | FlutterStandardTypedData typedDataWithInt32: |
Int64List | long[] | FlutterStandardTypedData typedDataWithInt64: |
Float64List | double[] | FlutterStandardTypedData typedDataWithFloat64: |
List | java.util.ArrayList | NSArray |
Map | java.util.HashMap | NSDictionary |
关于编解码器,Android端提供了以下四种。
BinaryCodec:是最简单的一种编解码器,其返回值类型与入参的类型相同,均为二进制格式(ByteBuffer
)。由于BinaryCodec
在编解码过程中什么都没做,只是原封不动的将二进制数据返回。所以传递的数据在编解码时会免于拷贝,这种方式在传递的数据量比较大时很有用。比如从Android侧传入一张图片到Flutter侧显示。
StandardMessageCodec:是BasicMessageChannel
的默认编解码器,支持基础数据类型、列表及字典等。在编码时会先将数据写入到ByteArrayOutputStream
流中,然后再将该流中的数据写入到ByteBuffer
中。在解码时,直接从ByteBuffer
中读取数据。
StandardMethodCodec:是基于StandardMessageCodec
的封装。是MethodChannel
与EventChannel
的默认编解码器。
StringCodec:是用于字符串与二进制数据之间的编解码,其编码格式为UTF-8。在编码时会将String转成byte数组,然后再将该数组写入到ByteBuffer
中。在解码时,直接从ByteBuffer
中读取数据
JSONMessageCodec:内部调用StringCodec
来实现编解码。
JSONMethodCodec:基于JSONMessageCodec
的封装。可以在MethodChannel
与EventChannel
中使用。
ByteBuffer
是Nio中的一个类,顾名思义——就是一块存储字节的区域。它有两个实现类——DirectByteBuffer
与HeapByteBuffer
,DirectByteBuffer
是直接在内存中开辟了一块区域来存储数据,而HeapByteBuffer
是在JVM堆中开辟一块区域来存储数据,所以要想数据在DirectByteBuffer
中与HeapByteBuffer
互通,就需要进行一次拷贝。关于ByteBuffer
的更多内容可以去阅读Java NIO 的前生今世 之三 NIO Buffer 详解这篇文章。
前面讲了Android与flutter通信的一些基础知识,下面就进入正题,来看Android如何与flutter进行通信。
Android与Flutter之间的通信共有四种实现方式。
由于在初始化flutter页面时会传递一个字符串——route,因此我们就可以拿route来做文章,传递自己想要传递的数据。该种方式仅支持单向数据传递且数据类型只能为字符串,无返回值。
通过EventChannel
来实现,EventChannel
仅支持数据单向传递,无返回值。
通过MethodChannel
来实现,MethodChannel
支持数据双向传递,有返回值。
通过BasicMessageChannel
来实现,BasicMessageChannel
支持数据双向传递,有返回值。
下面就来看一下这几种方式的使用。
主要是利用了创建flutter页面传递的route来做文章,笔者认为该种方式属于取巧,但还是可以用来传递数据。它的使用很简单,代码如下。
首先来看Android代码。
//第三个参数可以换成我们想要字符串。
FlutterView flutterView = Flutter.createView(this, getLifecycle(), "route");
在flutter中,我们只需要通过下面代码来获取值即可。
void main() => runApp(MyApp( initParams: window.defaultRouteName, ));class MyApp extends StatelessWidget { final String initParams;//既是前面传递的值——route MyApp({Key key, @required this.initParams}) : super(key: key); @override Widget build(BuildContext context) {...}}
通过该种方式就可以在初始化flutter时,Android给flutter传递数据。由于runApp
仅会调用一次,所以该种方式只能传递一次数据且数据只能是字符串。
使用
window
的相关API需要导入包dart:ui
EventChannel
是一种native向flutter发送数据的单向通信方式,flutter无法返回任何数据给native。主要用于native向flutter发送手机电量变化、网络连接变化、陀螺仪、传感器等。它的使用方式如下。
首先来看Android代码。
public class EventChannelPlugin implements EventChannel.StreamHandler { private static final String TAG = EventChannelPlugin.class.getSimpleName(); private EventChannel.EventSink eventSink; private Activity activity; static EventChannelPlugin registerWith(FlutterView flutterView) { EventChannelPlugin plugin = new EventChannelPlugin(flutterView); new EventChannel(flutterView, "EventChannelPlugin").setStreamHandler(plugin); return plugin; } private EventChannelPlugin(FlutterView flutterView) { this.activity = (Activity) flutterView.getContext(); } void send(Object params) { if (eventSink != null) { eventSink.success(params); } } void sendError(String str1, String str2, Object params) { if (eventSink != null) { eventSink.error(str1, str2, params); } } void cancel() { if (eventSink != null) { eventSink.endOfStream(); } } //第一个参数为flutter初始化EventChannel时返回的值,仅此一次 @Override public void onListen(Object o, EventChannel.EventSink eventSink) { this.eventSink = eventSink; Log.i(TAG, "eventSink:" + eventSink); Log.i(TAG, "Object:" + o.toString()); Toast.makeText(activity, "onListen——obj:" + o, Toast.LENGTH_SHORT).show(); } @Override public void onCancel(Object o) { Log.i(TAG, "onCancel:" + o.toString()); Toast.makeText(activity, "onCancel——obj:" + o, Toast.LENGTH_SHORT).show(); this.eventSink = null; }}
笔者对Android端代码做了一个简单的封装,还是很好理解的。下面就来看flutter代码实现。
class _MyHomePageState extends State { EventChannel _eventChannelPlugin = EventChannel("EventChannelPlugin"); StreamSubscription _streamSubscription; @override void initState() { _streamSubscription = _eventChannelPlugin //["abc", 123, "你好"]对应着Android端onListen方法的第一个参数,可不传值 .receiveBroadcastStream(["abc", 123, "你好"]) .listen(_onToDart, onError: _onToDartError, onDone: _onDone); super.initState(); } @override void dispose() { if (_streamSubscription != null) { _streamSubscription.cancel(); _streamSubscription = null; } super.dispose(); } //native端发送正常数据 void _onToDart(message) { print(message); } //当native出错时,发送的数据 void _onToDartError(error) { print(error); } //当native发送数据完成时调用的方法,每一次发送完成就会调用 void _onDone() { print("消息传递完毕"); } @override Widget build(BuildContext context) {...}}
上面就是通过EventChannel
来进行通信的代码实现,调用EventChannelPlugin
的send
方法就能给flutter发送数据。
MethodChannel
是一种native与flutter之间互相发送数据的通信方式,顾名思义,通过MethodChannel
就能调用native与flutter中相对应的方法,该种方式有返回值。它的使用方式如下。
首先来看Android端的代码实现。
public class MethodChannelPlugin implements MethodChannel.MethodCallHandler { private Activity activity; private MethodChannel channel; public static MethodChannelPlugin registerWith(FlutterView flutterView) { MethodChannel channel = new MethodChannel(flutterView, "MethodChannelPlugin"); MethodChannelPlugin methodChannelPlugin = new MethodChannelPlugin((Activity) flutterView.getContext(), channel); channel.setMethodCallHandler(methodChannelPlugin); return methodChannelPlugin; } private MethodChannelPlugin(Activity activity, MethodChannel channel) { this.activity = activity; this.channel = channel; } //调用flutter端方法,无返回值 public void invokeMethod(String method, Object o) { channel.invokeMethod(method, o); } //调用flutter端方法,有返回值 public void invokeMethod(String method, Object o, MethodChannel.Result result) { channel.invokeMethod(method, o, result); } @Override public void onMethodCall(MethodCall methodCall, MethodChannel.Result result) { switch (methodCall.method) { case "send"://返回的方法名 //给flutter端的返回值 result.success("MethodChannelPlugin收到:" + methodCall.arguments); Toast.makeText(activity, methodCall.arguments + "", Toast.LENGTH_SHORT).show(); if (activity instanceof FlutterAppActivity) { ((FlutterAppActivity) activity).showContent(methodCall.arguments); } break; default: result.notImplemented(); break; } }}
笔者对Android端代码做了一个简单的封装,还是很好理解的。下面就来看flutter代码实现。
class _MyHomePageState extends State { MethodChannel _methodChannel = MethodChannel("MethodChannelPlugin"); @override void initState() { _methodChannel.setMethodCallHandler((handler) => Future(() { print("_methodChannel:${handler}"); //监听native发送的方法名及参数 switch (handler.method) { case "send": _send(handler.arguments);//handler.arguments表示native传递的方法参数 break; } })); super.initState(); } //native调用的flutter方法 void _send(arg) { setState(() { _content = arg; }); } String _resultContent = ""; //flutter调用native的相应方法 void _sendToNative() { Future future = _methodChannel.invokeMethod("send", _controller.text); future.then((message) { setState(() { //message是native返回的数据 _resultContent = "返回值:" + message; }); }); } @override Widget build(BuildContext context) {...}}
上面就是通过MethodChannel
来进行通信的代码实现。还是比较简单的。在Android端使用只需要调用MethodChannelPlugin
的invokeMethod
方法即可。在flutter端使用只需要参考_sendToNative
方法的实现即可。
BasicMessageChannel
是一种能够在native与flutter之间互相发送消息的通信方式,它支持数据类型最多,使用范围最广。EventChannel
与MethodChannel
的应用场景可以使用BasicMessageChannel
来实现,但BasicMessageChannel
的应用场景就不一定能够使用EventChannel
与MethodChannel
来实现。该方式有返回值。它的使用方式如下。
首先来看Android代码的实现。
//这里支持的数据类型为String。
//这里支持的数据类型为String。public class BasicMessageChannelPlugin implements BasicMessageChannel.MessageHandler { private Activity activity; private BasicMessageChannel messageChannel; static BasicMessageChannelPlugin registerWith(FlutterView flutterView) { return new BasicMessageChannelPlugin(flutterView); } private BasicMessageChannelPlugin(FlutterView flutterView) { this.activity = (Activity) flutterView.getContext(); this.messageChannel = new BasicMessageChannel(flutterView, "BasicMessageChannelPlugin", StringCodec.INSTANCE); messageChannel.setMessageHandler(this); } @Override public void onMessage(String s, BasicMessageChannel.Reply reply) { reply.reply("BasicMessageChannelPlugin收到:" + s); if (activity instanceof FlutterAppActivity) { ((FlutterAppActivity) activity).showContent(s); } } void send(String str, BasicMessageChannel.Reply reply) { messageChannel.send(str, reply); }}
笔者对Android端代码做了一个简单的封装,还是很好理解的。下面就来看flutter代码实现。
class _MyHomePageState extends State { //StringCodec()为编码格式 BasicMessageChannel _basicMessageChannel = BasicMessageChannel("BasicMessageChannelPlugin", StringCodec()); @override void initState() { _basicMessageChannel.setMessageHandler((message) => Future(() { print(message); //message为native传递的数据 setState(() { _content = message; }); //给Android端的返回值 return "收到Native消息:" + message; })); _controller = TextEditingController(); super.initState(); } //向native发送消息 void _sendToNative() { Future future = _basicMessageChannel.send(_controller.text); future.then((message) { _resultContent = "返回值:" + message; }); } @override Widget build(BuildContext context) {...}}
上面就是通过BasicMessageChannel
来进行通信的代码实现。在Android端只需要调用BasicMessageChannelPlugin
的send
方法就可以向flutter发送数据,BasicMessageChannel.Reply
是返回值的回调方法。在flutter端使用只需要参考_sendToNative
方法的实现即可。
从分析Android与Flutter通信的源码来看,实现还是比较简单的,都是以ByteBuffer
为数据载体,然后通过BinaryMessenger
来发送与接收数据。整体设计如下。
从图中可以看出,Android侧与flutter侧采用了相同的设计。前面说过通信时是异步进行的,那么线程切换在哪?其实是在系统底层实现的。在Android与Flutter通信中,系统底层屏蔽了线程切换、数据拷贝等大量复杂操作。使得Android侧与flutter侧能方便的来进行通信。
在Android侧,BinaryMessenger
是一个接口,在FlutterView
中实现了该接口,在BinaryMessenger
的方法中通过JNI来与系统底层沟通。在Flutter侧,BinaryMessenger
是一个类,该类的作用就是与类window
沟通,而类window
才真正与系统底层沟通。
关于通信的底层实现可以去阅读闲鱼的技术文章——深入理解Flutter Platform Channel,这篇文章很好的讲述了Flutter与Native通信的系统底层原理。
在Android与Flutter混合开发模式下,相互之间通信的场景肯定不会少。了解Android与Flutter之间通信的各种方式及使用,有助于选用合理的方式来实现。
喜欢 就关注吧,欢迎投稿!
作者:大逗大人
链接:https://juejin.im/post/5d04790a6fb9a07efa09164d
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。