2019年10月8日 星期二

Flutter 與原生插件溝通

Flutter 與原生插件主要是透過通道來溝通,除了 Flutter 官網上所展示的 MethodChannel 外, 還有 EventChannel 及 MessageChannel.  他們之間的最大不同點是: EventChanel 僅能單向從原生插件端往 Flutter 平台推送資料流, 也就是說原生插件是主動發送端, Fultter 處於被動的接收資料流, 而 MethodChannel 主動權是在 Flutter 端, 原生插件處在被動回應的角色, 嚴格來說是半雙工通道(需求發送完要等待回應), 至於 MessageChannel, 視情況兩端都能各自指定為主動端或是被動端, 是一個全雙工的通道(發送與接收分開同時運作)在互相傳輸資料, 理論上來說 MessageChannel 是可以取代上述兩種通道的, 只是運作介面之不同而已.通常要馬上回應的溝通管道直接使用 MethodChannel, 需長時間的回應就就交由 EventChannel, 比方說錄影時要馬上回應機器狀態, 就直接通過 MethodChannele 溝通與控制, 當錄影開始到結束需要花費較長時間,不可能讓程序長時間等待,就可以另闢 EventChannel 管道,後續用廣播方式作通知. 用 EventChannl 與 MethodChannel 就能滿足大部份文字訊息溝通的需求, 關於 EventChannel 與 MessageChannel 的使用方式,參考範例程式:
 // 原生 flutter plugin 端 kotlin 原始碼
    import android.os.Bundle
    import io.flutter.app.FlutterActivity
    import io.flutter.plugin.common.StringCodec
    import io.flutter.plugin.common.MethodChannel
    import io.flutter.plugin.common.EventChannel
    import io.flutter.plugin.common.BasicMessageChannel
    import io.flutter.plugins.GeneratedPluginRegistrant    // ...   
    var stream : EventChannel.EventSink? = null
    EventChannel(flutterView , "com.example.event").setStreamHandler(//事件通道註冊
        object:  EventChannel.StreamHandler { // 繼承 StreamHandler 並實現
                      override fun onCancel(arguments: Any?) { stream = null  }
                      override fun onListen(arguments: Any?, sink: EventChannel.EventSink?) {
                            stream = sink
                            stream?.success("init complete")   // 資料流非同步發送一個事件
                     }
    })
    //... async send event
    stream?.success("event !") // 資料流非同步再發送一個事件
    val msg  = BasicMessageChannel<String>(flutterView, "channel", StringCodec.INSTANCE)//簡單訊息通道註冊
    msg.send("Hello!") { reply: String? -> println(reply); } // 簡單訊息通道發送一則訊息  
    msg.setMessageHandler { req, reply ->  reply.reply("$req: plugin")  }// 同時能回應訊息
    // ...

// Flutter 端 dart 原始碼
   final _event  =  EventChannel('com.example.event'); //事件通道註冊
   _event.receiveBroadcastStream().listen((onData) {  //監聽資料流所發生的事件
         print('$onData');
   });
   final _msg = BasicMessageChannel<String>('channel', StringCodec());//簡單訊息通道   
  _msg.setMessageHandler( (String onData) async { // 簡單訊息通道等待接收訊息  
        print('receive: $onData');
        return 'hi';  // 回應資料
    });
  _msg.send('who').then(( x) =>  print('answer:$x')); // 簡單訊息通道同時能傳送訊息
總結來說, EventChannel 專用於插件的廣播頻道, MethodChannel 專用於插件的回應需求通道, 而 MessageChannel 則是一個通用的全雙工溝通管道.
使用 BinaryMessenger 通道傳輸, 參考文章:
        https://medium.com/flutter/flutter-platform-channels-ce7f540a104e
底下使用 android 端的 Kotlin 原始碼:  android platform -> flutter 溝通:
    import android.os.Bundle
    import io.flutter.app.FlutterActivity
    io.flutter.plugin.common.BinaryMessenger
    import io.flutter.plugin.common.StringCodec
    import java.nio.ByteBuffer
    import java.nio.ByteOrder
    // import io.flutter.plugin.common.BinaryCodec
    // import io.flutter.plugin.common.StandardMessageCodec
    // 若要接收監聽處理範例:
    flutterView.setMessageHandler("binary")  { buffer: ByteBuffer?,  reply ->    
      buffer?.order(ByteOrder.nativeOrder())      
      buffer?.putDouble(3.1415)
      buffer?.putInt(123456789)
      reply.reply(buffer)
    }
   // 若要傳送並等待處理範例
    val buffer = ByteBuffer.allocateDirect(12)
    buffer.putDouble(3.1415)
    buffer.putInt(123456789)  
    flutterView.send("binary", buffer) { _ -> } // 忽略回傳值
    // {  x: ByteBuffer? -> val y =  BinaryCodec.INSTANCE.decodeMessage(x) }//處理回傳值

底下使用 Flutter 端的 dart 原始碼:  flutter  -> android platform 溝通:
    // 若要接收監聽處理範例:
    BinaryMessages.setMessageHandler('binary', (ByteData buffer) async {
      final readBuffer = ReadBuffer(buffer);
      final x = readBuffer.getFloat64( );
      final n = readBuffer.getInt32( );
      print('Received $x and $n');
      return null;
    });
    // 若要傳送並等待處理範例
    final writeBuffer = WriteBuffer( ) ..
                      putFloat64(3.1415) ..
                      putInt32(12345678);
    final buffer = writeBuffer.done();
    await BinaryMessages.send('binary', buffer);
上述 BinaryMessages 通道若是透過 StringCodec( ), 就能用字串加以溝通:
    // 若要接收監聽處理範例:
    final codec = StringCodec( );
    BinaryMessages.setMessageHandler('binary', (ByteData buffer) async {
      print('${codec.decodeMessage(buffer)}');
      return codec.encodeMessage('Hi');
    });
    // 若要傳送並等待處理範例:
    final reply = await BinaryMessages.send('binary', codec.encodeMessage('Hello'))
    print(codec.decodeMessage(reply));

沒有留言:

張貼留言

簡單 c 程式碼, 根據五行八卦相生相剋推斷吉凶

#include "stdio.h" // 五行: //               木2 //      水1           火3 //         金0     土4 // // 先天八卦 vs 五行 //                    ...