如需轉載,請註明出處: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), ), ); } }