本文首发于微信公众号「Android开发之旅」,欢迎关注 ,获取更多技术干货

通信场景

我们在做Flutter混合开发的时候通常需要进行Flutter和Native之间的通信。 比如Dart调用Native的相册选择图片,Native将电量、GPS信息主动传递给Dart等等。在混合开发中通信通常有以下几种:

  • 初始化Flutter时Native向Dart传递数据。
  • Native发送数据给Dart。
  • Dart发送数据给Native。
  • Dart发送数据给Native,然后Native回传数据给Dart。

第一种通信方式我们在讲解原生项目接入Flutter时已经讲解过,有兴趣的同学可以移步到Flutter混合开发(一):Android项目集成Flutter模块详细指南看下。

通信机制

Flutter与Native端之间的通信机制是通过Platform Channel来完成。消息使用Channel在Flutter端和Native端进行传递。具体如下图所示:

Flutter混合开发(三):Android与Flutter之间通信详细指南 - 图1

从图中可以看出,两端之间的通信都是双向的,而且是完成异步传递。Flutter定义了三种不同类型的Channel:

  • BasicMessageChannel:用于传递字符串或者半结构化的信息,持续通信,收到信息后可以进行回复。
  • MethodChannel:用于传递方法调用,一次性通信。通常用于Dart调用Native的方法。
  • EventChannel:用于数据流的通信,持续通信,收到消息后无法回复此次消息。通常用于Native向Dart的通信。

下面我们就来看看这三种Channel通信方式的具体使用和介绍。

BasicMessageChannel

Android端的相关方法:
  1. BasicMessageChannel(BinaryMessenger messenger, String name, MessageCodec<T> codec)
  • messenger参数是消息信使(FlutterView),是消息发送和接受的工具。
  • name参数是channel的名字也是其唯一标识,要和Dart端统一。
  • codec是消息编解码器,也需要和Dart端统一。它的作用就是将消息在发送的时候进行加密,dart端收到消息后在进行解密,传递的都是二进制数据。它有四种类型:BinaryCodec、StringCodec、JSONMessageCodec、StandardMessageCodec,四种类型均属于MessageCodec范畴。如不指定默认是StandardMessageCodec。

当我们需要接受来自Dart端发送的消息时使用setMessageHandler方法:

  1. void setMessageHandler(BasicMessageChannel.MessageHandler<T> handler)

参数handler是消息处理器,配合BinaryMessenger来完成对消息的处理。它是一个接口,具体的实现在onMessage方法中:

  1. public interface MessageHandler<T> {
  2. void onMessage(T message, BasicMessageChannel.Reply<T> reply);
  3. }

参数message即为Dart发送的数据, reply是用于回复消息的回调函数,提供reply.reply(“”)设置回复的内容。

上面讲的是接受Dart端的消息,那么Native端主动发送消息则是使用send方法,它有两个重载方法:

  1. void send(T message)
  2. void send(T message, BasicMessageChannel.Reply<T> callback)

参数message即为要发生给Dart的数据,callback回调则是用于接收Dart端收到消息后的回复信息。

Dart端相关方法:
  1. const BasicMessageChannel(this.name, this.codec);

这里的name和codec和Android端的构造方法参数是一样的,那么是channel的名字也是唯一标识,codec是消息编解码器,两个参数在两端必须统一。

Dart端如果要接受Native端的消息则要设置setMessageHandler方法:

  1. void setMessageHandler(Future<T> handler(T message))

参数handler为消息处理器,配合BinaryMessenger来完成对消息的处理。

通过send方法向Native端发送消息:

  1. Future<T> send(T message)

参数message为要传递的参数。Future为发送消息后等待Native回复的回调函数。

BasicMessageChannel实战:Android端和Flutter端相互发送消息,并且在收到消息后返回对方信息

Android端代码:
  1. //初始化BasicMessageChannel
  2. BasicMessageChannel<String> basicMessageChannel = new BasicMessageChannel<>(flutterView,
  3. "BasicMessageChannelPlugin",StringCodec.INSTANCE);
  4. //接受消息
  5. basicMessageChannel.setMessageHandler((message, reply) -> {
  6. mTvDart.setText(message);
  7. reply.reply("收到dart数据:接受成功");
  8. });
  9. //发送消息
  10. basicMessageChannel.send(message, reply -> mTvDart.setText(reply));

Dart端代码:
  1. //初始化BasicMessageChannel
  2. static const BasicMessageChannel<String> _basicMessageChannel =
  3. BasicMessageChannel("BasicMessageChannelPlugin", StringCodec());
  4. // 接受消息
  5. void handleBasicMessageChannel() {
  6. _basicMessageChannel
  7. .setMessageHandler((String message) => Future<String>(() {
  8. setState(() {
  9. showMessage = message;
  10. });
  11. return "收到Native的消息:接受成功";
  12. }));
  13. }
  14. //发送消息
  15. response = await _basicMessageChannel.send(_controller.text);
  16. setState(() {
  17. showMessage = response;
  18. });

最后效果为下图,红色分割线上部分为Native页面,下部分为Flutter页面。

Flutter混合开发(三):Android与Flutter之间通信详细指南 - 图2

MethodChannel

使用MethodChannel相关方法的参数类型及含义和BasicMessageChannel的参数含义都是相同的,下面就不一一解释了。

Androd端相关方法:
  1. MethodChannel(BinaryMessenger messenger, String name)
  2. MethodChannel(BinaryMessenger messenger, String name, MethodCodec codec)

第一个构造函数会构造一个StandardMethodCodec.INSTANCE类型的MethodCodec。MethodCodec定义了两种类型:JSONMethodCodec和StandardMethodCodec。

如果想接受来自Dart端的消息则使用:

  1. setMethodCallHandler(@Nullable MethodChannel.MethodCallHandler handler)

MethodCallHandler为接口,回调方法为:

  1. public interface MethodCallHandler {
  2. void onMethodCall(MethodCall call, MethodChannel.Result result);
  3. }

call参数有两个成员变量,String类型的call.method表示调用的方法名,Object类型的call.arguments表示调用方法所传递的入参。result是回复此消息的回调函数提供了result.success,result.error,result.notImplemented方法调用。

发送消息主动调用Dart代码则使用invokeMethod方法

  1. invokeMethod(@NonNull String method, @Nullable Object arguments)
  2. invokeMethod(String method, @Nullable Object arguments, Result callback)

第二个方法多了一个callback,它是用来接受Dart端收到消息后的回复信息。

  1. public interface Result {
  2. void success(@Nullable Object result);
  3. void error(String errorCode, @Nullable String errorMessage, @Nullable Object errorDetails);
  4. void notImplemented();
  5. }

Dart端相关方法:
  1. const MethodChannel(this.name, [this.codec = const StandardMethodCodec()])

构造函数默认是使用StandardMethodCodec编解码器。

通过setMethodCallHandler方法接受来自Native的方法调用,通过invokeMethod方法调用Native端的方法。

  1. void setMethodCallHandler(Future<dynamic> handler(MethodCall call))
  1. Future<T> invokeMethod<T>(String method, [ dynamic arguments ])

MethodChannel实战: Native端调用Dart端的getPlatform方法返回当前的os平台,Dart端调用Native端的getBatteryLevel方法获取当前手机电量。

Android端代码:
  1. //初始化MethodChannel
  2. MethodChannel methodChannel = new MethodChannel(flutterView, "MethodChannelPlugin");
  3. mBtnTitle.setOnClickListener(new View.OnClickListener() {
  4. @Override
  5. public void onClick(View v) {
  6. //调用dart端getPlatform方法
  7. methodChannel.invokeMethod("getPlatform", null, new MethodChannel.Result() {
  8. @Override
  9. public void success(@Nullable Object result) {
  10. mTvDart.setText(result.toString());
  11. }
  12. @Override
  13. public void error(String errorCode, @Nullable String errorMessage, @Nullable Object errorDetails) {
  14. mTvDart.setText(errorCode + "==" + errorMessage);
  15. }
  16. @Override
  17. public void notImplemented() {
  18. mTvDart.setText("未实现getPlatform方法");
  19. }
  20. });
  21. }
  22. });
  23. //接受dart的调用
  24. methodChannel.setMethodCallHandler((call, result) -> {
  25. switch (call.method) {
  26. case "getBatteryLevel":
  27. int batteryLevel = getBatteryLevel();
  28. if (batteryLevel != -1) {
  29. result.success("电量为:" + batteryLevel);
  30. } else {
  31. result.error("1001", "调用错误", null);
  32. }
  33. break;
  34. default:
  35. result.notImplemented();
  36. break;
  37. }
  38. });

Dart端代码:
  1. // receive
  2. void handleMethodChannelReceive() {
  3. Future<dynamic> platformCallHandler(MethodCall call) async {
  4. switch (call.method) {
  5. case "getPlatform":
  6. return getPlatformName(); //调用success方法
  7. // return PlatformException(code: '1002',message: "出现异常"); //调用error
  8. break;
  9. }
  10. }
  11. _methodChannel.setMethodCallHandler(platformCallHandler);
  12. // _methodChannel.setMethodCallHandler(null); //调用notImplemented
  13. }
  14. //send
  15. void handleMethodChannelSend() async {
  16. try {
  17. response = await _methodChannel.invokeMethod("getBatteryLevel");
  18. print(response);
  19. setState(() {
  20. showMessage = response;
  21. });
  22. } catch (e) {
  23. //捕获error和notImplemented异常
  24. setState(() {
  25. showMessage = e.message;
  26. });
  27. }
  28. }

当我们在使用setMethodCallHandler接受到native的消息时,直接调用相关方法即可调用Native端的success回调。

如果直接抛异常如PlatformException,那么就调用Native端的error回调。

  1. PlatformException(code: '1002',message: "出现异常")

如果我们直接设置handler为null

  1. _methodChannel.setMethodCallHandler(null);

那么就会调用Native端的notImplemented方法回调。

同理我们在Dart端使用invokeMethod方法是,需要进行异常捕获以便于我们接受到Native端调用的error和notImplemented方法回调。

最后效果为下图,红色分割线上部分为Native页面,下部分为Flutter页面。

Flutter混合开发(三):Android与Flutter之间通信详细指南 - 图3

EventChannel

EventChannel内部实现原理其实也是通过MethodChannel来完成的。

Android端相关代码:
  1. EventChannel(BinaryMessenger messenger, String name)
  2. EventChannel(BinaryMessenger messenger, String name, MethodCodec codec)

同样的,也是两个构造,默认codec为StandardMethodCodec,EventChannel和MethodChannel的codec都属于MethodCodec范畴。

通过setStreamHandler来监听Dart端发送的消息,

  1. void setStreamHandler(EventChannel.StreamHandler handler)

其中handler是一个接口:

  1. public interface StreamHandler {
  2. void onListen(Object args, EventChannel.EventSink eventSink);
  3. void onCancel(Object o);
  4. }

args为dart端初始化监听流的参数,eventSink设置了三个回调,分别是success、error和endofStream。分别对应Dart端的ondata、error和onDone回调。

Dart端相关代码:
  1. const EventChannel(this.name, [this.codec = const StandardMethodCodec()]);

通过EventChannel初始化一个channel对象。如果从Native中接受数据需要定义一个广播流:

  1. Stream<dynamic> receiveBroadcastStream([ dynamic arguments ])

通过调用Stream的listen方法来完成注册。

EventChannel实战:Native端主动发送电量信息给Dart端,Dart端收到信息后进行展示。

Android端代码:
  1. EventChannel eventChannel = new EventChannel(flutterView, "EventChannelPlugin");
  2. eventChannel.setStreamHandler(new EventChannel.StreamHandler() {
  3. @Override
  4. public void onListen(Object arguments, EventChannel.EventSink events) {
  5. events.success(arguments.toString() + getBatteryLevel());
  6. //events.error("111","出现错误","");
  7. //events.endOfStream();
  8. }
  9. @Override
  10. public void onCancel(Object arguments) {
  11. }
  12. });

Dart端代码:
  1. //init
  2. static const EventChannel _eventChannel = EventChannel("EventChannelPlugin");
  3. //receive
  4. void handleEventChannelReceive() {
  5. streamSubscription = _eventChannel
  6. .receiveBroadcastStream() //可以携带参数
  7. .listen(_onData, onError: _onError, onDone: _onDone);
  8. }
  9. void _onDone() {
  10. setState(() {
  11. showMessage = "endOfStream";
  12. });
  13. }
  14. _onError(error) {
  15. setState(() {
  16. PlatformException platformException = error;
  17. showMessage = platformException.message;
  18. });
  19. }
  20. void _onData(message) {
  21. setState(() {
  22. showMessage = message;
  23. });
  24. }

通过event.success方法发送信息,dart端通过监听Stream流来获取信息。当Native端调用events.error时在Dart端的onError回调中需要将error转换为PlatformException才能获取到异常的相关信息。

最后效果为下图,红色分割线上部分为Native页面,下部分为Flutter页面。

Flutter混合开发(三):Android与Flutter之间通信详细指南 - 图4

总结

主要是讲解了Android端和Dart的三种通信方式。详细分析了方法构成和具体的实例使用。每一种方式都对应不同的使用场景,大家可以按需选择,多加练习做到熟能生巧。

文中都是贴的一些代码片段,全部Demo源码已经上传到后台,关注公众号回复「混合开发」即可获取下载链接。