引言
我們通過 plugin 來實現 flutter 端與 native 端的通訊。主要體現在方法的相互呼叫以及資料流的傳送監聽。今天我們來記錄一下這兩種互動的實現方式:MethodChannel
和 EventChannel
。
MethodChannel
:用於方法呼叫EventChannel
:用於資料流(event streams)的通訊,(監聽,傳送)
我們需要一個 demo
建立一個簡單的 demo ,頁面三個元素:
receiveData
文字塊:監聽顯示從 native 端接收到的資料流startTimer
按鈕:flutter 端呼叫 native 端,開啟事件輪詢stopTimer
按鈕:flutter 端呼叫 native 端,關閉事件輪詢
demo 裡,flutter 按鍵通過 methodChannel 呼叫 native 層方法,開啟了 timer 輪詢,native 端每隔1秒,將當前時間戳回傳到 eventChannel 資料流中。
開始 demo 建立:
-
native 端:
建立 plugin 類繼承 FlutterPlugin
class PluginCommunicationDemoPlugin : FlutterPlugin, MethodCallHandler, EventChannel.StreamHandler {
// methodChannel 和 eventChannel 的路徑定義,通過這個路徑進行通訊
companion object {
const val METHOD_CHANNEL_PATH = "rex_method_channel"
const val EVENT_CHANNEL_PATH = "rex_event_channel"
}
private lateinit var methodChannel: MethodChannel
private lateinit var eventChannel: EventChannel
private var eventSink: EventChannel.EventSink? = null
private var mWorker: TimeWorker = TimeWorker(object : TimerWorkCallback {
override fun onResult(data: String) {
sendEventToStream(data)
}
})
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
methodChannel = MethodChannel(flutterPluginBinding.binaryMessenger, METHOD_CHANNEL_PATH)
methodChannel.setMethodCallHandler(this)
eventChannel = EventChannel(flutterPluginBinding.binaryMessenger, EVENT_CHANNEL_PATH)
eventChannel.setStreamHandler(this)
}
// methodChannel 方法呼叫
override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
when (call.method) {
"startTimer" -> {
result.success(mWorker.startTimer())
}
"stopTimer" -> {
result.success(mWorker.stopTimer())
}
else -> {
result.notImplemented()
}
}
}
//推送訊息給Event資料流,flutter層負責監聽資料流
fun sendEventToStream(data: String) {
Handler(Looper.getMainLooper()).post {
eventSink?.success(data)
}
}
override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
methodChannel.setMethodCallHandler(null)
}
override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
// eventChannel 建立連線
eventSink = events
}
override fun onCancel(arguments: Any?) {
eventSink = null
}
}
複製程式碼
- onMethodCall 方法進行 methodChannel 事件分發,通過 result.success(any) 進行事件結果回傳
- 通過 EventChannel.EventSink 將資料注入到 event 資料流中,flutter層進行監聽
- TimeWorker 控制的輪詢開啟停止,下面附上具體程式碼
class TimeWorker {
private val format = SimpleDateFormat("yyyy-MM-DD HH:mm:ss")
var timer: Timer? = null
var callback: TimerWorkCallback? = null
constructor(callback: TimerWorkCallback?) {
this.callback = callback
}
//開始事件流傳送
fun startTimer(): Boolean {
stopTimer()
if (timer == null) {
timer = Timer()
}
timer?.schedule(object : TimerTask() {
@RequiresApi(Build.VERSION_CODES.O)
override fun run() {
callback?.onResult(getCurrentTimeStr())
}
}, Calendar.getInstance().time, 1000)
return true
}
//停止事件流
fun stopTimer(): Boolean {
timer?.cancel()
timer = null
return true
}
private fun getCurrentTimeStr(): String {
return format.format(Calendar.getInstance().time)
}
}
interface TimerWorkCallback {
fun onResult(data: String)
}
複製程式碼
-
Flutter 端:
將 methodChannel 、eventChannel 封裝成工具類:
import 'package:flutter/services.dart';
abstract class TestPluginTool {
static const MethodChannel _methodChannel = const MethodChannel("rex_method_channel");
static const EventChannel _eventChannel = const EventChannel("rex_event_channel");
//開啟 native 輪詢
static Future<bool> startTimer() async {
return await _methodChannel.invokeMethod("startTimer");
}
//關閉 native 輪詢
static Future<bool> stopTimer() async {
return await _methodChannel.invokeMethod("stopTimer");
}
//監聽 native event 資料流
static void onListenStreamData(onEvent, onError) {
_eventChannel.receiveBroadcastStream().listen(onEvent, onError: onError);
}
}
複製程式碼
非常簡單,一一對應,匹配 methodChannel ,eventChannel 的路徑
-
最後一步,完成 UI:
class _MyAppState extends State<MyApp> {
var _receivedData = "";
@override
void initState() {
super.initState();
_onReceiveEventData();
}
// 開啟輪詢
void _startTimer() {
TestPluginTool.startTimer().then((value) => print("startTimer success"));
}
// 關閉輪詢
void _stopTimer() {
TestPluginTool.startTimer().then((value) => print("stopTimer success"));
}
/// 監聽 eventChannel 資料流
void _onReceiveEventData() {
TestPluginTool.onListenStreamData((data) {
setState(() {
_receivedData = data;
});
}, (error) {
print("event channel error : $error");
});
}
@override
Widget build(BuildContext context) {
return Container(
alignment: Alignment.center,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("receiveData : $_receivedData"),
TextButton(
onPressed: () {
_startTimer();
},
child: Text("startTimer")),
TextButton(
onPressed: () {
_stopTimer();
},
child: Text("stopTimer")),
],
),
);
}
}
複製程式碼