flutter: 如何使用 MethodChannel 和 EventChannel

引言

我們通過 plugin 來實現 flutter 端與 native 端的通信。主要體現在方法的相互調用以及數據流的發送監聽。今天我們來記錄一下這兩種交互的實現方式:MethodChannelEventChannel

  • MethodChannel:用于方法調用
  • EventChannel:用于數據流(event streams)的通信,(監聽,發送)

我們需要一個 demo

創建一個簡單的 demo ,頁面三個元素:


demo_UI.png
  • 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")),
        ],
      ),
    );
  }
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,119評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,382評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,038評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,853評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,616評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,112評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,192評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,355評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,869評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,727評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,928評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,467評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,165評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,570評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,813評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,585評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,892評論 2 372

推薦閱讀更多精彩內容