如需转载,请注明出处:Flutter学习笔记(29)--Flutter如何与native进行通讯html
前言:在咱们开发Flutter项目的时候,不免会遇到须要调用native api或者是其余的状况,这时候就须要处理Flutter与native的通讯问题,通常经常使用的Flutter与native的通讯方式有3中。android
1.MethodChannel:Flutter端向native端发送通知,一般用来调用native的某一个方法。api
2.EventChannel:用于数据流的通讯,有监听功能,好比电量变化后直接推送给Flutter端。app
3.BasicMessageChannel:用于传递字符串或半结构体的数据。async
接下来具体看一下每种通讯方式的使用方法!ide
先来总体说一下逻辑思想吧,这样能更容易理解一些,若是想要实现Flutter与native通讯,首先要创建一个通讯的通道,经过一个通道标识来进行匹配,匹配上了以后Flutter端经过invokeMethod调用方法来发起一个请求,在native端经过onMethodCall进行匹配请求的key,匹配上了就处理对应case内的逻辑!!!总体来看,我感受有点EventBus的意思呢,就像是一条事件总线。。。post
第一步:实现通讯插件Plugin-native端学习
因为一个项目中可能会须要不少Flutter与native的通讯,因此我这里是将测试的插件封装到一个类里面了,而后在MainActivity里面的onCreate进行注册测试
package com.example.flutter_demo; import android.content.Context; import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; import io.flutter.plugin.common.PluginRegistry; public class TestPlugin implements MethodChannel.MethodCallHandler { public static String CHANNELNAME = "channel_name";//每个通讯通道的惟一标识,在整个项目内惟一!!! private static MethodChannel methodChannel; private Context context; public TestPlugin(Context context) { this.context = context; } public static void registerWith(PluginRegistry.Registrar registrar){ methodChannel = new MethodChannel(registrar.messenger(),CHANNELNAME); TestPlugin instance = new TestPlugin(registrar.activity()); methodChannel.setMethodCallHandler(instance); } @Override public void onMethodCall(MethodCall methodCall, MethodChannel.Result result) { if (methodCall.method.equals("method_key")){ result.success("what is up man???"); } } }
注:CHANNELNAME-->上面说过了,因为项目内会有不少的通讯,因此咱们定义的Channel必须是惟一的!!!!ui
TestPlugin实现MethodChannel.MethodCallHandler,定义一个对外暴露的注册方法registerWith,由于咱们须要在MainActivity进行注册,在registerWith方法内初始化MethodChannel
接下来咱们看一下onMethodCall方法,这个方法在Flutter发起请求时被调用,方法内有两个参数,一个methodCall和一个result,咱们分别来讲一下这两个参数:
methodCall:其中当前请求的相关信息,好比匹配请求的key
result:用于给Flutter返回数据,有3个方法,result.success(成功调用)、result.erro(失败调用)、result.notImplemented(方法没有实现调用)
第二步:注册通讯插件Plugin-native端
package com.example.flutter_demo; import android.os.Bundle; import io.flutter.app.FlutterActivity; import io.flutter.plugins.GeneratedPluginRegistrant; public class MainActivity extends FlutterActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); GeneratedPluginRegistrant.registerWith(this); TestPlugin.registerWith(this.registrarFor(TestPlugin.CHANNELNAME)); } }
注册这块我感受做用是起到了一个桥梁的做用,经过注册将插件和Flutter内定义的CHANNEL关联了起来。
第三步:Flutter内发起通讯请求-flutter端
import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; void main() => runApp(MyApp()); class MyApp extends StatefulWidget{ @override State<StatefulWidget> createState() { // TODO: implement createState return new MyAppState(); } } class MyAppState extends State<MyApp> { var _textContent = 'welcome to flutter word'; Future<Null> _changeTextContent() async{ //channel_name每个通讯通道的惟一标识,在整个项目内惟一!!! const platfom = const MethodChannel('channel_name'); try { //method_key是插件TestPlugin中onMethodCall回调匹配的key String resultValue = await platfom.invokeMethod('method_key'); setState(() { _textContent = resultValue; }); }on PlatformException catch (e){ print(e.toString()); } } @override Widget build(BuildContext context) { // TODO: implement build return new MaterialApp( theme: new ThemeData( primaryColor: Colors.white, ), debugShowCheckedModeBanner: false, title: 'demo', home: new Scaffold( appBar: new AppBar( title: new Text('Demo'), leading: Icon(Icons.menu,size: 30,), actions: <Widget>[ Icon(Icons.search,size: 30,) ], ), body: new Center( child: new Text(_textContent), ), floatingActionButton: new FloatingActionButton(onPressed: _changeTextContent,child: new Icon(Icons.adjust),), ), ); } }
这里的功能就是页面中央有一个text,经过点击一个按钮,发起通讯请求,通讯成功在就收到native返回的数据后将text的文案修改。
咱们看一下最终的效果:
MethodChannel通讯是双向的,也就是说,Flutter端能够向native发起通讯,native也能够向Flutter端发起通讯,本质上就是反过来调用一下,原理上是同一个意思,具体的代码就不在这里写了,须要的话能够自行百度一下!
EventChannel的使用咱们也以官方获取电池电量的demo为例,手机的电池状态是不停变化的。咱们要把这样的电池状态变化由Native及时经过EventChannel来告诉Flutter。这种状况用以前讲的MethodChannel办法是不行的,这意味着Flutter须要用轮询的方式不停调用getBatteryLevel来获取当前电量,显然是不正确的作法。而用EventChannel的方式,则是将当前电池状态"推送"给Flutter。
第一步:MainActivity内注册EventChannel,并提供获取电量的方法-native端
public class EventChannelPlugin implements EventChannel.StreamHandler { private Handler handler; private static final String CHANNEL = "com.example.flutter_battery/stream"; private int count = 0; public static void registerWith(PluginRegistry.Registrar registrar) { // 新建 EventChannel, CHANNEL常量的做用和 MethodChannel 同样的 final EventChannel channel = new EventChannel(registrar.messenger(), CHANNEL); // 设置流的处理器(StreamHandler) channel.setStreamHandler(new EventChannelPlugin()); } @Override public void onListen(Object o, EventChannel.EventSink eventSink) { // 每隔一秒数字+1 handler = new Handler(message -> { // 而后把数字发送给 Flutter eventSink.success(++count); handler.sendEmptyMessageDelayed(0, 1000); return false; }); handler.sendEmptyMessage(0); } @Override public void onCancel(Object o) { handler.removeMessages(0); handler = null; count = 0; } }
其中onCancel表明对面再也不接收,这里咱们应该作一些clean up的事情。而 onListen则表明通道已经建好,Native能够发送数据了。注意onListen里带的EventSink这个参数,后续Native发送数据都是通过EventSink的。
第二步:同MethodChannel同样,发起通讯请求
class _MyHomePageState extends State<MyHomePage> { // 建立 EventChannel static const stream = const EventChannel('com.example.flutter_battery/stream'); int _count = 0; StreamSubscription _timerSubscription; void _startTimer() { if (_timerSubscription == null) // 监听 EventChannel 流, 会触发 Native onListen回调 _timerSubscription = stream.receiveBroadcastStream().listen(_updateTimer); } void _stopTimer() { _timerSubscription?.cancel(); _timerSubscription = null; setState(() => _count = 0); } void _updateTimer(dynamic count) { print("--------$count"); setState(() => _count = count); } @override void dispose() { super.dispose(); _timerSubscription?.cancel(); _timerSubscription = null; } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: Container( margin: EdgeInsets.only(left: 10, top: 10), child: Center( child: Column( children: [ Row( children: <Widget>[ RaisedButton( child: Text('Start EventChannel', style: TextStyle(fontSize: 12)), onPressed: _startTimer, ), Padding( padding: EdgeInsets.only(left: 10), child: RaisedButton( child: Text('Cancel EventChannel', style: TextStyle(fontSize: 12)), onPressed: _stopTimer, )), Padding( padding: EdgeInsets.only(left: 10), child: Text("$_count"), ) ], ) ], ), ), ), ); } }
总体说明一下:Flutter端经过stream.receiveBroadcastStream().listen监听native发送过来的数据,native端经过eventSink.success(++count)不断的将数据返回给Flutter端,这样就实现了咱们想要的实时监听的效果了!
其实他就是一个简版的MethodChannel,也能够说MethodChannel是基于BasicMessageChannel实现的,BasicMessageChannel只是进行通讯,更通俗的理解就是两端发通知,可是不须要进行方法匹配。
第一步:初始化及注册-native
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 省略其余代码... messageChannel = new BasicMessageChannel<>(flutterView, CHANNEL, StringCodec.INSTANCE); messageChannel. setMessageHandler(new MessageHandler<String>() { @Override public void onMessage(String s, Reply<String> reply) { // 接收到Flutter消息, 更新Native onFlutterIncrement(); reply.reply(EMPTY_MESSAGE); } }); FloatingActionButton fab = findViewById(R.id.button); fab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // 通知 Flutter 更新 sendAndroidIncrement(); } }); } private void sendAndroidIncrement() { messageChannel.send(PING); } private void onFlutterIncrement() { counter++; TextView textView = findViewById(R.id.button_tap); String value = "Flutter button tapped " + counter + (counter == 1 ? " time" : " times"); textView.setText(value); }
第二步:Flutter端发起通讯-flutter
class _MyHomePageState extends State<MyHomePage> { static const String _channel = 'increment'; static const String _pong = 'pong'; static const String _emptyMessage = ''; static const BasicMessageChannel<String> platform = BasicMessageChannel<String>(_channel, StringCodec()); int _counter = 0; @override void initState() { super.initState(); // 设置消息处理器 platform.setMessageHandler(_handlePlatformIncrement); } // 若是接收到 Native 的消息 则数字+1 Future<String> _handlePlatformIncrement(String message) async { setState(() { _counter++; }); // 发送一个空消息 return _emptyMessage; } // 点击 Flutter 中的 FAB 则发消息给 Native void _sendFlutterIncrement() { platform.send(_pong); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('BasicMessageChannel'), ), body: Container( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[ Expanded( child: Center( child: Text( 'Platform button tapped $_counter time${_counter == 1 ? '' : 's'}.', style: const TextStyle(fontSize: 17.0)), ), ), Container( padding: const EdgeInsets.only(bottom: 15.0, left: 5.0), child: Row( children: <Widget>[ Image.asset('assets/flutter-mark-square-64.png', scale: 1.5), const Text('Flutter', style: TextStyle(fontSize: 30.0)), ], ), ), ], )), floatingActionButton: FloatingActionButton( onPressed: _sendFlutterIncrement, child: const Icon(Icons.add), ), ); } }