前言
Flutter 作為混合開發,跟native端做一些交互在所難免,比如說調用原生系統傳感器、原生端的網絡框架進行數據請求就會用到 Flutter 調用android 及android 原生調用 Flutter的方法,這里就涉及到Platform Channels(平臺通道)
Platform Channels (平臺通道)
Flutter 通過Channel 與客戶端之間傳遞消息,如圖:
圖中就是通過MethodChannel的方式實現Flutter 與客戶端之間的消息傳遞。MethodChannel是Platform Channels中的一種,Flutter有三種通信類型:
BasicMessageChannel:用于傳遞字符串和半結構化的信息
MethodChannel:用于傳遞方法調用(method invocation)通常用來調用native中某個方法
EventChannel: 用于數據流(event streams)的通信。有監聽功能,比如電量變化之后直接推送數據給flutter端。
為了保證UI的響應,通過Platform Channels傳遞的消息都是異步的。
更多關于channel原理可以去看這篇文章:channel原理篇
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<MyHomePage> {
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: <Widget>[
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<Null> _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<dynamic> 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<dynamic> 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<Object> basicMessageChannel = new BasicMessageChannel<>(getFlutterView(), BASIC_CHANNEL, StandardMessageCodec.INSTANCE);
//主動發送消息到flutter 并接收flutter消息回復
basicMessageChannel.send("send basic message", (object)-> {
Log.e(TAG, "receive reply msg from flutter:" + object.toString());
});
//接收flutter消息 并發送回復
basicMessageChannel.setMessageHandler((object, reply)-> {
Log.e(TAG, "receive msg from flutter:" + object.toString());
reply.reply("reply:got your message");
});
}
BasicMessageChannel Flutter端
static const basicChannel = const BasicMessageChannel('common.flutter/basic', StandardMessageCodec());
//發送消息到原生客戶端 并且接收到原生客戶端的回復
Future<String> 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端的消息通信方式。
以上相關代碼demo