引言
我們通過 plugin 來(lái)實(shí)現(xiàn) flutter 端與 native 端的通信。主要體現(xiàn)在方法的相互調(diào)用以及數(shù)據(jù)流的發(fā)送監(jiān)聽。今天我們來(lái)記錄一下這兩種交互的實(shí)現(xiàn)方式:MethodChannel
和 EventChannel
-
MethodChannel
:用于方法調(diào)用 -
EventChannel
:用于數(shù)據(jù)流(event streams)的通信旗笔,(監(jiān)聽尊浓,發(fā)送)
我們需要一個(gè) demo
創(chuàng)建一個(gè)簡(jiǎn)單的 demo 过吻,頁(yè)面三個(gè)元素:
-
receiveData
文本塊:監(jiān)聽顯示從 native 端接收到的數(shù)據(jù)流 -
startTimer
按鈕:flutter 端調(diào)用 native 端电抚,開啟事件輪詢 -
stopTimer
按鈕:flutter 端調(diào)用 native 端惕稻,關(guān)閉事件輪詢
demo 里,flutter 按鍵通過 methodChannel 調(diào)用 native 層方法蝙叛,開啟了 timer 輪詢俺祠,native 端每隔1秒,將當(dāng)前時(shí)間戳回傳到 eventChannel 數(shù)據(jù)流中借帘。
開始 demo 創(chuàng)建:
-
native 端:
創(chuàng)建 plugin 類繼承 FlutterPlugin
class PluginCommunicationDemoPlugin : FlutterPlugin, MethodCallHandler, EventChannel.StreamHandler {
// methodChannel 和 eventChannel 的路徑定義蜘渣,通過這個(gè)路徑進(jìn)行通信
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 方法調(diào)用
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數(shù)據(jù)流,flutter層負(fù)責(zé)監(jiān)聽數(shù)據(jù)流
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 方法進(jìn)行 methodChannel 事件分發(fā)肺然,通過 result.success(any) 進(jìn)行事件結(jié)果回傳
- 通過 EventChannel.EventSink 將數(shù)據(jù)注入到 event 數(shù)據(jù)流中蔫缸,flutter層進(jìn)行監(jiān)聽
- 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
}
//開始事件流發(fā)送
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");
}
//關(guān)閉 native 輪詢
static Future<bool> stopTimer() async {
return await _methodChannel.invokeMethod("stopTimer");
}
//監(jiān)聽 native event 數(shù)據(jù)流
static void onListenStreamData(onEvent, onError) {
_eventChannel.receiveBroadcastStream().listen(onEvent, onError: onError);
}
}
非常簡(jiǎn)單拾碌,一一對(duì)應(yīng),匹配 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"));
}
// 關(guān)閉輪詢
void _stopTimer() {
TestPluginTool.startTimer().then((value) => print("stopTimer success"));
}
/// 監(jiān)聽 eventChannel 數(shù)據(jù)流
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")),
],
),
);
}
}