Flutter 混合开发(Android)Flutter跟Native相互通信
前言
Flutter 作为混合开发,跟native端做一些交互在所难免,比如说调用原生系统传感器、原生端的网络框架进行数据请求就会用到 Flutter 调用android 及android 原生调用 Flutter的方法,这里就涉及到Platform Channels(平台通道)
Platform Channels (平台通道)
Flutter 通过Channel 与客户端之间传递消息,如图:
image.png
图中就是通过MethodChannel的方式实现Flutter 与客户端之间的消息传递。MethodChannel是Platform Channels中的一种,Flutter有三种通信类型:
BasicMessageChannel:用于传递字符串和半结构化的信息MethodChannel:用于传递方法调用(method invocation)通常用来调用native中某个方法EventChannel: 用于数据流(event streams)的通信。有监听功能,比如电量变化之后直接推送数据给flutter端。
为了保证UI的响应,通过Platform Channels传递的消息都是异步的。
Platform Channels 使用
1.MethodChannel的使用
原生客户端写法(以Android 为例)
首先定义一个获取手机电量方法
private int getBatteryLevel() { return 90; }
这函数是要给Flutter 调用的方法,此时就需要通过 MethodChannel
来建立这个通道了。
首先新增一个初始化 MethodChannel
的方法
private String METHOD_CHANNEL = "common.flutter/battery";private String GET_BATTERY_LEVEL = "getBatteryLevel";private MethodChannel methodChannel;@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); GeneratedPluginRegistrant.registerWith(this); initMethodChannel(); getFlutterView().postDelayed(() -> methodChannel.invokeMethod("get_message", null, new MethodChannel.Result() { @Override public void success(@Nullable Object o) { Log.d(TAG, "get_message:" + o.toString()); } @Override public void error(String s, @Nullable String s1, @Nullable Object o) { } @Override public void notImplemented() { } }), 5000); } private void initMethodChannel() { methodChannel = new MethodChannel(getFlutterView(), METHOD_CHANNEL); methodChannel.setMethodCallHandler( (methodCall, result) -> { if (methodCall.method.equals(GET_BATTERY_LEVEL)) { int batteryLevel = getBatteryLevel(); if (batteryLevel != -1) { result.success(batteryLevel); } else { result.error("UNAVAILABLE", "Battery level not available.", null); } } else { result.notImplemented(); } }); } private int getBatteryLevel() { return 90; }
METHOD_CHANNEL
用于和flutter交互的标识,由于一般情况下会有多个channel,在app里面需要保持唯一性
MethodChannel
都是保存在以通道名为Key的Map中。所以要是设了两个名字一样的channel,只有后设置的那个会生效。
onMethodCall
有两个参数,onMethodCall
里包含要调用的方法名称和参数。Result是给Flutter的返回值。方法名是客户端与Flutter统一设定。通过if/switch语句判断 MethodCall.method
来区分不同的方法,在我们的例子里面我们只会处理名为“getBatteryLevel”的调用。在调用本地方法获取到电量以后通过 result.success(batteryLevel)
调用把电量值返回给Flutter。
MethodChannel-Flutter 端
直接先看一下Flutter端的代码
class _MyHomePageState extends State { int _counter = 0; static const platform = const MethodChannel('common.flutter/battery'); void _incrementCounter() { setState(() { _counter++; _getBatteryLevel(); }); } @override Widget build(BuildContext context) { platform.setMethodCallHandler(platformCallHandler); return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( 'You have pushed the button this many times:', ), Text( '$_counter', style: Theme.of(context).textTheme.display1, ), Text('$_batteryLevel'), ], ), ), floatingActionButton: FloatingActionButton( onPressed: _incrementCounter, tooltip: 'Increment', child: Icon(Icons.add), ), ); } String _batteryLevel = 'Unknown battery level.'; Future _getBatteryLevel() async { String batteryLevel; try { final int result = await platform.invokeMethod('getBatteryLevel'); batteryLevel = 'Battery level at $result % .'; } on PlatformException catch (e) { batteryLevel = "Failed to get battery level: '${e.message}'."; } setState(() { _batteryLevel = batteryLevel; }); } //客户端调用 Future platformCallHandler(MethodCall call) async { switch (call.method) { case "get_message": return "Hello from Flutter"; break; } }}
上面代码解析:
首先,定义一个常量result.success(platform)
,和Android客户端定义的channel一致;
接下来定义一个 result.success(_getBatteryLevel())
方法,用来调用Android 端的方法,result.success(final int result = await platform.invokeMethod('getBatteryLevel');)
这行代码就是通过通道来调用Native(Android)方法了。因为MethodChannel是异步调用的,所以这里必须要使用await关键字。
在上面Android代码中我们把获取到的电量通过result.success(batteryLevel);
返回给Flutter。这里await表达式执行完成以后电量就直接赋值给result变量了。然后通过result.success(setState);
去改变Text显示值。到这里为止,是通过Flutter端调用原生客户端方法。
MethodChannel
其实是一个可以双向调用的方法,在上面的代码中,其实我们也体现了,通过原生客户端调用Flutter的方法。
在原生端通过 methodChannel.invokeMethod
的方法调用
methodChannel.invokeMethod("get_message", null, new MethodChannel.Result() { @Override public void success(@Nullable Object o) { Log.d(TAG, "get_message:" + o.toString()); } @Override public void error(String s, @Nullable String s1, @Nullable Object o) { } @Override public void notImplemented() { } });
在Flutter端就需要给MethodChannel
设置一个MethodCallHandler
static const platform = const MethodChannel('common.flutter/battery');platform.setMethodCallHandler(platformCallHandler);Future platformCallHandler(MethodCall call) async { switch (call.method) { case "get_message": return "Hello from Flutter"; break; } }
以上就是MethodChannel
的相关用法了。
EventChannel
将数据推送给Flutter端,类似我们常用的推送功能,有需要就推送给Flutter端,是否需要去处理这个推送由Flutter那边决定。相对于MethodChannel
是主动获取,EventChannel
则是被动推送。
EventChannel 原生客户端写法
private String EVENT_CHANNEL = "common.flutter/message";private int count = 0;private Timer timer;private void initEventChannel() { new EventChannel(getFlutterView(), EVENT_CHANNEL).setStreamHandler(new EventChannel.StreamHandler() { @Override public void onListen(Object arguments, EventChannel.EventSink events) { timer.schedule(new TimerTask() { @Override public void run() { if (count < 10) { count++; events.success("当前时间:" + System.currentTimeMillis()); } else { timer.cancel(); } } }, 1000, 1000); } @Override public void onCancel(Object o) { } }); }
在上面的代码中,我们做了一个定时器,每秒向Flutter推送一个消息,告诉Flutter我们当前时间。为了防止一直倒计时,我这边做了个计数,超过10次就停止发送。
EventChannel Flutter端
String message = "not message";static const eventChannel = const EventChannel('common.flutter/message');@override void initState() { super.initState(); eventChannel.receiveBroadcastStream().listen(_onEvent, onError: _onError); }void _onEvent(Object event) { setState(() { message = "message: $event"; }); } void _onError(Object error) { setState(() { message = 'message: unknown.'; }); }
上面的代码就是Flutter端接收原生客户端数据,通过_onEvent
来接收数据,将数据显示Text
。这个实现相对简单,如果要达到业务分类,需要将数据封装成json,通过json数据包装一些对应业务标识和数据来做区分。
BasicMessageChannel
BasicMessageChannel (主要是传递字符串和一些半结构体的数据)
BasicMessageChannel Android端
private void initBasicMessageChannel() { BasicMessageChannel
BasicMessageChannel Flutter端
static const basicChannel = const BasicMessageChannel('common.flutter/basic', StandardMessageCodec());//发送消息到原生客户端 并且接收到原生客户端的回复 Future sendMessage() async { String reply = await basicChannel.send('this is flutter'); print("receive reply msg from native:$reply"); return reply; } //接收原生消息 并发送回复 void receiveMessage() async { basicChannel.setMessageHandler((msg) async { print("receive from Android:$msg"); return "get native message"; });
上面例子中用到的编解码器为StandardMessageCodec ,例子中通信都是String,用StringCodec也可以。
以上就是Flutter提供三种platform和dart端的消息通信方式。
更多相关文章
- Android(安卓)总结:进阶之路(资源与方法)
- Android面试复习(Android篇一)
- Android(安卓)按钮点击事件监听的3重方式
- Instrumentation 框架简介
- Activity的启动流程(基于Android(安卓)10.0源码)
- Android(安卓)系统启动过程详解
- 利用HTML5开发Android
- 浅谈Java中Collections.sort对List排序的两种方法
- Python list sort方法的具体使用