flutter: 如何使用 MethodChannel 和 EventChannel

引言

我們通過 plugin 來(lái)實(shí)現(xiàn) flutter 端與 native 端的通信。主要體現(xiàn)在方法的相互調(diào)用以及數(shù)據(jù)流的發(fā)送監(jiān)聽。今天我們來(lái)記錄一下這兩種交互的實(shí)現(xiàn)方式:MethodChannelEventChannel

  • MethodChannel:用于方法調(diào)用
  • EventChannel:用于數(shù)據(jù)流(event streams)的通信旗笔,(監(jiān)聽尊浓,發(fā)送)

我們需要一個(gè) demo

創(chuàng)建一個(gè)簡(jiǎn)單的 demo 过吻,頁(yè)面三個(gè)元素:


demo_UI.png
  • 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")),
        ],
      ),
    );
  }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市灾前,隨后出現(xiàn)的幾起案子防症,更是在濱河造成了極大的恐慌,老刑警劉巖哎甲,帶你破解...
    沈念sama閱讀 206,723評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蔫敲,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡烧给,警方通過查閱死者的電腦和手機(jī)燕偶,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)础嫡,“玉大人指么,你說我怎么就攤上這事×穸Γ” “怎么了伯诬?”我有些...
    開封第一講書人閱讀 152,998評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)巫财。 經(jīng)常有香客問我盗似,道長(zhǎng),這世上最難降的妖魔是什么平项? 我笑而不...
    開封第一講書人閱讀 55,323評(píng)論 1 279
  • 正文 為了忘掉前任赫舒,我火速辦了婚禮悍及,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘接癌。我一直安慰自己心赶,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,355評(píng)論 5 374
  • 文/花漫 我一把揭開白布缺猛。 她就那樣靜靜地躺著缨叫,像睡著了一般。 火紅的嫁衣襯著肌膚如雪荔燎。 梳的紋絲不亂的頭發(fā)上耻姥,一...
    開封第一講書人閱讀 49,079評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音有咨,去河邊找鬼琐簇。 笑死,一個(gè)胖子當(dāng)著我的面吹牛摔吏,可吹牛的內(nèi)容都是我干的鸽嫂。 我是一名探鬼主播纵装,決...
    沈念sama閱讀 38,389評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼征讲,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了橡娄?” 一聲冷哼從身側(cè)響起诗箍,我...
    開封第一講書人閱讀 37,019評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎挽唉,沒想到半個(gè)月后滤祖,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,519評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡瓶籽,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,971評(píng)論 2 325
  • 正文 我和宋清朗相戀三年匠童,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片塑顺。...
    茶點(diǎn)故事閱讀 38,100評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡汤求,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出严拒,到底是詐尸還是另有隱情扬绪,我是刑警寧澤,帶...
    沈念sama閱讀 33,738評(píng)論 4 324
  • 正文 年R本政府宣布裤唠,位于F島的核電站挤牛,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏种蘸。R本人自食惡果不足惜墓赴,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,293評(píng)論 3 307
  • 文/蒙蒙 一竞膳、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧诫硕,春花似錦顶猜、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至纲菌,卻和暖如春挠日,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背翰舌。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工嚣潜, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人椅贱。 一個(gè)月前我還...
    沈念sama閱讀 45,547評(píng)論 2 354
  • 正文 我出身青樓懂算,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親庇麦。 傳聞我的和親對(duì)象是個(gè)殘疾皇子计技,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,834評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容