在不得不看的Flutter与Android混合开发一文中讲述了Android项目如何导入flutter模块,但有一个问题,就是它们之间还不能互相通讯,但这又是很是必要的。因此本文就来说述一下Android如何与flutter进行通讯。html
消息经过平台通道在native(host)与flutter(client)之间传递,以下图所示: java
为了确保用户界面可以正确响应,消息都是以异步的方式进行传递。不管是native向flutter发送消息,仍是flutter向native发送消息。segmentfault
在flutter中,MethodChannel能够发送与方法调用相对应的消息。在native平台上,MethodChannel在Android能够接收方法调用并返回结果。这些类能够帮助咱们用不多的代码就能开发平台插件。api
注意:本节内容来自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端提供了如下四种。less
ByteBuffer
)。因为BinaryCodec
在编解码过程当中什么都没作,只是原封不动的将二进制数据返回。因此传递的数据在编解码时会免于拷贝,这种方式在传递的数据量比较大时颇有用。好比从Android侧传入一张图片到Flutter侧显示。BasicMessageChannel
的默认编解码器,支持基础数据类型、列表及字典等。在编码时会先将数据写入到ByteArrayOutputStream
流中,而后再将该流中的数据写入到ByteBuffer
中。在解码时,直接从ByteBuffer
中读取数据。StandardMessageCodec
的封装。是MethodChannel
与EventChannel
的默认编解码器。ByteBuffer
中。在解码时,直接从ByteBuffer
中读取数据StringCodec
来实现编解码。JSONMessageCodec
的封装。能够在MethodChannel
与EventChannel
中使用。
ByteBuffer
是Nio中的一个类,顾名思义——就是一块存储字节的区域。它有两个实现类——DirectByteBuffer
与HeapByteBuffer
,DirectByteBuffer
是直接在内存中开辟了一块区域来存储数据,而HeapByteBuffer
是在JVM堆中开辟一块区域来存储数据,因此要想数据在DirectByteBuffer
中与HeapByteBuffer
互通,就须要进行一次拷贝。关于ByteBuffer
的更多内容能够去阅读Java NIO 的前生今世 之三 NIO Buffer 详解这篇文章。异步
前面讲了Android与flutter通讯的一些基础知识,下面就进入正题,来看Android如何与flutter进行通讯。ide
Android与Flutter之间的通讯共有四种实现方式。
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<MyHomePage> {
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<MyHomePage> {
MethodChannel _methodChannel = MethodChannel("MethodChannelPlugin");
@override
void initState() {
_methodChannel.setMethodCallHandler((handler) => Future<String>(() {
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<String> 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。
public class BasicMessageChannelPlugin implements BasicMessageChannel.MessageHandler<String> {
private Activity activity;
private BasicMessageChannel<String> messageChannel;
static BasicMessageChannelPlugin registerWith(FlutterView flutterView) {
return new BasicMessageChannelPlugin(flutterView);
}
private BasicMessageChannelPlugin(FlutterView flutterView) {
this.activity = (Activity) flutterView.getContext();
this.messageChannel = new BasicMessageChannel<String>(flutterView, "BasicMessageChannelPlugin", StringCodec.INSTANCE);
messageChannel.setMessageHandler(this);
}
@Override
public void onMessage(String s, BasicMessageChannel.Reply<String> reply) {
reply.reply("BasicMessageChannelPlugin收到:" + s);
if (activity instanceof FlutterAppActivity) {
((FlutterAppActivity) activity).showContent(s);
}
}
void send(String str, BasicMessageChannel.Reply<String> reply) {
messageChannel.send(str, reply);
}
}
复制代码
笔者对Android端代码作了一个简单的封装,仍是很好理解的。下面就来看flutter代码实现。
class _MyHomePageState extends State<MyHomePage> {
//StringCodec()为编码格式
BasicMessageChannel<String> _basicMessageChannel =
BasicMessageChannel("BasicMessageChannelPlugin", StringCodec());
@override
void initState() {
_basicMessageChannel.setMessageHandler((message) => Future<String>(() {
print(message);
//message为native传递的数据
setState(() {
_content = message;
});
//给Android端的返回值
return "收到Native消息:" + message;
}));
_controller = TextEditingController();
super.initState();
}
//向native发送消息
void _sendToNative() {
Future<String> future = _basicMessageChannel.send(_controller.text);
future.then((message) {
_resultContent = "返回值:" + message;
});
}
@override
Widget build(BuildContext context) {...}
}
复制代码
上面就是经过BasicMessageChannel
来进行通讯的代码实现。在Android端只须要调用BasicMessageChannelPlugin
的send
方法就能够向flutter发送数据,BasicMessageChannel.Reply<String>
是返回值的回调方法。在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之间通讯的各类方式及使用,有助于选用合理的方式来实现。