原生Android与Flutter通信方式(一)

96
aimaile
2019.04.21 18:32* 字数 1436

通信在任何程序中都是举足轻重的,在做Flutter开发的时候通常离不了Flutter和Native之间的通信,比如:初始化Flutter时Native向Dart传递数据,Dart调用Native的相册选择图片,Dart调用Native的模块进行一些复杂的计算,Native将一些数据(GPS信息,陀螺仪,传感器等)主动传递给Dart等。当然,这种通信的感觉有点像原生app调用C/C++,不用担心这里不需要写jni,不需要会cmake,但是这里需要懂原生app基本开发了,同理ios也是一样的。

言归正传,这里将向大家介绍在Flutter中Dart和Native之间通信的几种方式以及其原理和使用技巧;接下来我将分场景来介绍Dart 和Native之间的通信。
几种通信场景:
1、初始化Flutter时Native向Dart传递数据;
2、Native发送数据给Dart;
3、Dart发送数据给Native;
4、Dart发送数据给Native,然后Native回传数据给Dart;

Flutter 与 Native通信机制

在讲解Flutter 与 Native之间是如何传递数据之前,我们先来了解下 Flutter与Native的通信机制,Flutter和Native的通信是通过Channel来完成的。

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


flutter通信.png

Flutter中消息的传递是完全异步的;

Channel所支持的数据类型对照表:

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:
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

Flutter定义了三种不同类型的Channel:

BasicMessageChannel:用于传递字符串和半结构化的信息,持续通信,收到消息后可以回复此次消息,如:Native将遍历到的文件信息陆续传递到Dart,在比如:Flutter将从服务端陆陆续获取到信息交个Native加工,Native处理完返回等;

MethodChannel:用于传递方法调用(method invocation)一次性通信:如Flutter调用Native拍照;

EventChannel: 用于数据流(event streams)的通信,持续通信,收到消息后无法回复此次消息,通常用于Native向Dart的通信,如:手机电量变化,网络连接变化,陀螺仪,传感器等;

这三种类型的类型的Channel都是全双工通信,即A <=> B,Dart可以主动发送消息给platform端,并且platform接收到消息后可以做出回应,同样,platform端可以主动发送消息给Dart端,dart端接收数后返回给platform端。

BasicMessageChannel用法

Native端:

构造方法

BasicMessageChannel(BinaryMessenger messenger, String name, MessageCodec<T> codec)
  • BinaryMessenger messenger - 消息信使,是消息的发送与接收的工具;
  • String name - Channel的名字,也是其唯一标识符;
  • MessageCodec<T> codec - 消息的编解码器,它有几种不同类型的实现:
  • BinaryCodec - 最为简单的一种Codec,因为其返回值类型和入参的类型相同,均为二进制格式(Android中为ByteBuffer,iOS中为NSData)。实际上,BinaryCodec在编解码过程中什么都没做,只是原封不动将二进制数据消息返回而已。或许你会因此觉得BinaryCodec没有意义,但是在某些情况下它非常有用,比如使用BinaryCodec可以使传递内存数据块时在编解码阶段免于内存拷贝;
  • StringCodec - 用于字符串与二进制数据之间的编解码,其编码格式为UTF-8;
  • JSONMessageCodec - 用于基础数据与二进制数据之间的编解码,其支持基础数据类型以及列表、字典。其在iOS端使用了NSJSONSerialization作为序列化的工具,而在Android端则使用了其自定义的JSONUtil与StringCodec作为序列化工具;
  • StandardMessageCodec - 是BasicMessageChannel的默认编解码器,其支持基础数据类型、二进制数据、列表、字典,其工作原理;

setMessageHandler方法

void setMessageHandler(BasicMessageChannel.MessageHandler<T> handler)
  • BasicMessageChannel.MessageHandler<T> handler - 消息处理器,配合BinaryMessenger完成消息的处理;
    在创建好BasicMessageChannel后,如果要让其接收Dart发来的消息,则需要调用它的setMessageHandler方法为其设置一个消息处理器。

BasicMessageChannel.MessageHandler原型

public interface MessageHandler<T> {
    void onMessage(T var1, BasicMessageChannel.Reply<T> var2);
}
  • onMessage(T var1, BasicMessageChannel.Reply<T> var2) - 用于接受消息,var1是消息内容,var2是回复此消息的回调函数;

send方法原型

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

  • T message - 要传递给Dart的具体信息;
  • BasicMessageChannel.Reply<T> callback - 消息发出去后,收到Dart的回复的回调函数;

在创建好BasicMessageChannel后,如果要向Dart发送消息,可以调用它的send方法向Dart传递数据。

public class BasicMessageChannelPlugin implements BasicMessageChannel.MessageHandler<String>, BasicMessageChannel.Reply<String> {
    private final Activity activity;
    private final 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<>(flutterView, "BasicMessageChannelPlugin", StringCodec.INSTANCE);
        //设置消息处理器,处理来自Dart的消息
        messageChannel.setMessageHandler(this);
    }

    @Override
    public void onMessage(String s, BasicMessageChannel.Reply<String> reply) {//处理Dart发来的消息
        reply.reply("BasicMessageChannel收到:" + s);//可以通过reply进行回复
        if (activity instanceof IShowMessage) {
            ((IShowMessage) activity).onShowMessage(s);
        }
        Toast.makeText(activity, s, Toast.LENGTH_SHORT).show();
    }

    /**
     * 向Dart发送消息,并接受Dart的反馈
     *
     * @param message  要给Dart发送的消息内容
     * @param callback 来自Dart的反馈
     */
    void send(String message, BasicMessageChannel.Reply<String> callback) {
        messageChannel.send(message, callback);
    }

    @Override
    public void reply(String s) {

    }
}

Dart端:

构造方法原型

const BasicMessageChannel(this.name, this.codec);
  • String name - Channel的名字,要和Native端保持一致;
  • MessageCodec<T> codec - 消息的编解码器,要和Native端保持一致,有四种类型的实现具体可以参考Native端的介绍;

setMessageHandler方法原型

void setMessageHandler(Future<T> handler(T message))
  • Future<T> handler(T message) - 消息处理器,配合BinaryMessenger完成消息的处理;

在创建好BasicMessageChannel后,如果要让其接收Native发来的消息,则需要调用它的setMessageHandler方法为其设置一个消息处理器。

send方法原型

 Future<T> send(T message)
  • T message - 要传递给Native的具体信息;
  • Future<T> - 消息发出去后,收到Native回复的回调函数;

在创建好BasicMessageChannel后,如果要向Native发送消息,可以调用它的send方法向Native传递数据。

import 'package:flutter/services.dart';

static const BasicMessageChannel _basicMessageChannel =
      const BasicMessageChannel('BasicMessageChannelPlugin', StringCodec());

//使用BasicMessageChannel接受来自Native的消息,并向Native回复
_basicMessageChannel
    .setMessageHandler((String message) => Future<String>(() {
          setState(() {
            showMessage = message;
          });
          return "收到Native的消息:" + message;
        }));
//使用BasicMessageChannel向Native发送消息,并接受Native的回复
String response;
    try {
       response = await _basicMessageChannel.send(value);
    } on PlatformException catch (e) {
      print(e);
    }
flutter