Flutter 做爲混合開發,跟native端作一些交互在所不免,好比說調用原生系統傳感器、原生端的網絡框架進行數據請求就會用到 Flutter 調用android 及android 原生調用 Flutter的方法,這裏就涉及到Platform Channels(平臺通道)android
Flutter 經過Channel 與客戶端之間傳遞消息,如圖:json
圖中就是經過MethodChannel的方式實現Flutter 與客戶端之間的消息傳遞。MethodChannel是Platform Channels中的一種,Flutter有三種通訊類型:bash
BasicMessageChannel:用於傳遞字符串和半結構化的信息
MethodChannel:用於傳遞方法調用(method invocation)一般用來調用native中某個方法
EventChannel: 用於數據流(event streams)的通訊。有監聽功能,好比電量變化以後直接推送數據給flutter端。
複製代碼
爲了保證UI的響應,經過Platform Channels傳遞的消息都是異步的。網絡
更多關於channel原理能夠去看這篇文章:channel原理篇併發
首先定義一個獲取手機電量方法app
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裏面須要保持惟一性async
MethodChannel
都是保存在以通道名爲Key的Map中。因此要是設了兩個名字同樣的channel,只有後設置的那個會生效。ide
onMethodCall
有兩個參數,onMethodCall
裏包含要調用的方法名稱和參數。Result是給Flutter的返回值。方法名是客戶端與Flutter統一設定。經過if/switch語句判斷 MethodCall.method
來區分不一樣的方法,在咱們的例子裏面咱們只會處理名爲「getBatteryLevel」的調用。在調用本地方法獲取到電量之後經過 result.success(batteryLevel)
調用把電量值返回給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
的相關用法了。
將數據推送給Flutter端,相似咱們經常使用的推送功能,有須要就推送給Flutter端,是否須要去處理這個推送由Flutter那邊決定。相對於MethodChannel
是主動獲取,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次就中止發送。
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 (主要是傳遞字符串和一些半結構體的數據)
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");
});
}
複製代碼
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端的消息通訊方式。