Flutter 混合开发(三): Android 与 Flutter 之间通信详细指南 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
winlee28
V2EX    Android

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

  •  
  •   winlee28 2019-12-13 16:18:49 +08:00 13168 次点击
    这是一个创建于 2182 天前的主题,其中的信息可能已经有所发展或是发生改变。

    欢迎大家扫描下方二维码关注公众号,获取更多技术干货。

    为防止文章中的图片链接失效或者显示失败,这里贴下原文地址

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

    本文首发于微信公众号「 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 定义了三种不同类型的 Channel:

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

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

    BasicMessageChannel

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

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

    void setMessageHandler(BasicMessageChannel.MessageHandler<T> handler) 

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

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

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

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

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

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

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

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

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

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

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

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

     Future<T> send(T message) 

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

    BasicMessageChannel 实战:Android 端和 Flutter 端相互发送消息,并且在收到消息后返回对方信息
    Android 端代码:
     //初始化 BasicMessageChannel BasicMessageChannel<String> basicMessageChannel = new BasicMessageChannel<>(flutterView, "BasicMessageChannelPlugin",StringCodec.INSTANCE); //接受消息 basicMessageChannel.setMessageHandler((message, reply) -> { mTvDart.setText(message); reply.reply("收到 dart 数据:接受成功"); }); //发送消息 basicMessageChannel.send(message, reply -> mTvDart.setText(reply)); 
    Dart 端代码:
    //初始化 BasicMessageChannel static const BasicMessageChannel<String> _basicMessageChannel = BasicMessageChannel("BasicMessageChannelPlugin", StringCodec()); // 接受消息 void handleBasicMessageChannel() { _basicMessageChannel .setMessageHandler((String message) => Future<String>(() { setState(() { showMessage = message; }); return "收到 Native 的消息:接受成功"; })); } //发送消息 respOnse= await _basicMessageChannel.send(_controller.text); setState(() { showMessage = response; }); 

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

    MethodChannel

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

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

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

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

    setMethodCallHandler(@Nullable MethodChannel.MethodCallHandler handler) 

    MethodCallHandler 为接口,回调方法为:

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

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

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

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

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

    public interface Result { void success(@Nullable Object result); void error(String errorCode, @Nullable String errorMessage, @Nullable Object errorDetails); void notImplemented(); } 
    Dart 端相关方法:
    const MethodChannel(this.name, [this.codec = const StandardMethodCodec()]) 

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

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

     void setMethodCallHandler(Future<dynamic> handler(MethodCall call)) 
     Future<T> invokeMethod<T>(String method, [ dynamic arguments ]) 
    MethodChannel 实战:Native 端调用 Dart 端的 getPlatform 方法返回当前的 os 平台,Dart 端调用 Native 端的 getBatteryLevel 方法获取当前手机电量。
    Android 端代码:
    //初始化 MethodChannel MethodChannel methodChannel = new MethodChannel(flutterView, "MethodChannelPlugin"); mBtnTitle.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //调用 dart 端 getPlatform 方法 methodChannel.invokeMethod("getPlatform", null, new MethodChannel.Result() { @Override public void success(@Nullable Object result) { mTvDart.setText(result.toString()); } @Override public void error(String errorCode, @Nullable String errorMessage, @Nullable Object errorDetails) { mTvDart.setText(errorCode + "==" + errorMessage); } @Override public void notImplemented() { mTvDart.setText("未实现 getPlatform 方法"); } }); } }); //接受 dart 的调用 methodChannel.setMethodCallHandler((call, result) -> { switch (call.method) { case "getBatteryLevel": int batteryLevel = getBatteryLevel(); if (batteryLevel != -1) { result.success("电量为:" + batteryLevel); } else { result.error("1001", "调用错误", null); } break; default: result.notImplemented(); break; } }); 
    Dart 端代码:
    // receive void handleMethodChannelReceive() { Future<dynamic> platformCallHandler(MethodCall call) async { switch (call.method) { case "getPlatform": return getPlatformName(); //调用 success 方法 // return PlatformException(code: '1002',message: "出现异常"); //调用 error break; } } _methodChannel.setMethodCallHandler(platformCallHandler); // _methodChannel.setMethodCallHandler(null); //调用 notImplemented } //send void handleMethodChannelSend() async { try { respOnse= await _methodChannel.invokeMethod("getBatteryLevel"); print(response); setState(() { showMessage = response; }); } catch (e) { //捕获 error 和 notImplemented 异常 setState(() { showMessage = e.message; }); } } 

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

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

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

    如果我们直接设置 handler 为 null

    _methodChannel.setMethodCallHandler(null); 

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

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

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

    EventChannel

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

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

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

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

    void setStreamHandler(EventChannel.StreamHandler handler) 

    其中 handler 是一个接口:

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

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

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

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

    Stream<dynamic> receiveBroadcastStream([ dynamic arguments ]) 

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

    EventChannel 实战:Native 端主动发送电量信息给 Dart 端,Dart 端收到信息后进行展示。
    Android 端代码:
    EventChannel eventChannel = new EventChannel(flutterView, "EventChannelPlugin"); eventChannel.setStreamHandler(new EventChannel.StreamHandler() { @Override public void onListen(Object arguments, EventChannel.EventSink events) { events.success(arguments.toString() + getBatteryLevel()); //events.error("111","出现错误",""); //events.endOfStream(); } @Override public void onCancel(Object arguments) { } }); 
    Dart 端代码:
    //init static const EventChannel _eventChannel = EventChannel("EventChannelPlugin"); //receive void handleEventChannelReceive() { streamSubscription = _eventChannel .receiveBroadcastStream() //可以携带参数 .listen(_onData, onError: _onError, onDone: _onDone); } void _onDone() { setState(() { showMessage = "endOfStream"; }); } _onError(error) { setState(() { PlatformException platformException = error; showMessage = platformException.message; }); } void _onData(message) { setState(() { showMessage = message; }); } 

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

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

    总结

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

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

    扫描下方二维码关注公众号,获取更多技术干货。

    推荐阅读

    Flutter 开发必备 Dart 基础:Dart 快速入门

    Flutter 混合开发(一):Android 项目集成 Flutter 模块详细指南

    Flutter 混合开发(二):iOS 项目集成 Flutter 模块详细指南

    目前尚无回复
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5163 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 34ms UTC 07:21 PVG 15:21 LAX 23:21 JFK 02:21
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86