色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

Flutter與原生Native的三種交互方式

谷歌開發者 ? 來源:谷歌開發者 ? 作者:谷歌開發者 ? 2022-01-25 12:22 ? 次閱讀

之前我們介紹了如何在 Native (Android 項目) 中啟動 Flutter,展示 Flutter 頁面。但是在開發過程中,很多時候并不是簡單的展示一個頁面即可,還會涉及到各種交互,比如傳遞一些消息。

本篇文章就簡單介紹一下 Flutter 與原生 Native 的三種交互方式:

1.BasicMessageChannel

2.MethodChannel

3.EventChannel

BasicMessageChannel

雖然說是三種交互方式,但是其實本質都是一種,這個我們后面會解釋。

先來看看 BasicMessageChannel。它可以實現雙方交互,發送一些簡單消息,消息類型 Object,但是并不是所有 Object 都可以,基礎類型及基礎類型的數組、list、map 是可以的。這個可以參考 BasicMessageChannel 的源碼:

  public void send(@Nullable T message, @Nullable final Reply callback) {    messenger.send(        name,        codec.encodeMessage(message),        callback == null ? null : new IncomingReplyHandler(callback));  }
可以看到進行了 encode,這個 codec 一般是 StandardMessageCodec,它的 encodeMessage 函數源碼:
  public ByteBuffer encodeMessage(Object message) {    if (message == null) {      return null;    }    final ExposedByteArrayOutputStream stream = new ExposedByteArrayOutputStream();    writeValue(stream, message);    final ByteBuffer buffer = ByteBuffer.allocateDirect(stream.size());    buffer.put(stream.buffer(), 0, stream.size());    return buffer;  }

這里 writeValue 的源碼:

protected void writeValue(ByteArrayOutputStream stream, Object value) {    if (value == null || value.equals(null)) {      stream.write(NULL);    } else if (value == Boolean.TRUE) {      stream.write(TRUE);    } else if (value == Boolean.FALSE) {      stream.write(FALSE);    } else if (value instanceof Number) {      if (value instanceof Integer || value instanceof Short || value instanceof Byte) {        stream.write(INT);        writeInt(stream, ((Number) value).intValue());      } else if (value instanceof Long) {        stream.write(LONG);        writeLong(stream, (long) value);      } else if (value instanceof Float || value instanceof Double) {        stream.write(DOUBLE);        writeAlignment(stream, 8);        writeDouble(stream, ((Number) value).doubleValue());      } else if (value instanceof BigInteger) {        stream.write(BIGINT);        writeBytes(stream, ((BigInteger) value).toString(16).getBytes(UTF8));      } else {        throw new IllegalArgumentException("Unsupported Number type: " + value.getClass());      }    } else if (value instanceof String) {      stream.write(STRING);      writeBytes(stream, ((String) value).getBytes(UTF8));    } else if (value instanceof byte[]) {      stream.write(BYTE_ARRAY);      writeBytes(stream, (byte[]) value);    } else if (value instanceof int[]) {      stream.write(INT_ARRAY);      final int[] array = (int[]) value;      writeSize(stream, array.length);      writeAlignment(stream, 4);      for (final int n : array) {        writeInt(stream, n);      }    } else if (value instanceof long[]) {      stream.write(LONG_ARRAY);      final long[] array = (long[]) value;      writeSize(stream, array.length);      writeAlignment(stream, 8);      for (final long n : array) {        writeLong(stream, n);      }    } else if (value instanceof double[]) {      stream.write(DOUBLE_ARRAY);      final double[] array = (double[]) value;      writeSize(stream, array.length);      writeAlignment(stream, 8);      for (final double d : array) {        writeDouble(stream, d);      }    } else if (value instanceof List) {      stream.write(LIST);      final List list = (List) value;      writeSize(stream, list.size());      for (final Object o : list) {        writeValue(stream, o);      }    } else if (value instanceof Map) {      stream.write(MAP);      final Map map = (Map) value;      writeSize(stream, map.size());      for (final Entry entry : map.entrySet()) {        writeValue(stream, entry.getKey());        writeValue(stream, entry.getValue());      }    } else {      throw new IllegalArgumentException("Unsupported value: " + value);    }  }

下面看一下如何來使用它,以 Android 端為例。

Android 端(1) 不使用 engine cache 預熱

如果不使用 engine cache,那么在 FlutterActivity 的繼承類中重寫 configureFlutterEngine:

classMainActivity:FlutterActivity(){varchannel:BasicMessageChannel?= null    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {        super.configureFlutterEngine(flutterEngine)        var channel = BasicMessageChannel(flutterEngine.dartExecutor.binaryMessenger,"test" ,StringCodec.INSTANCE)        channel.setMessageHandler { message, reply ->Log.e("recieve",message)        }    }}

注意這里第二個參數 "test" 是這通道 (channel) 的名稱,兩邊名稱一致才能進行通信

第三個參數是消息的編解碼器,這里我們因為是簡單的示例,消息是字符串 String,所以用 StringCodec。

StringCodec 是 MessageCodec 接口的實現,除了它還有 BinaryCodec,JsonMessageCodec,StandardMessageCodec。另外我們還可以自己實現 MessageCodec,實現它的兩個函數即可,它的源碼如下:

public interface MessageCodec<T> {  /**   * Encodes the specified message into binary.   *   * @param message the T message, possibly null.   * @return a ByteBuffer containing the encoding between position 0 and the current position, or   *     null, if message is null.   */  @Nullable  ByteBuffer encodeMessage(@Nullable T message);
  /**   * Decodes the specified message from binary.   *   * @param message the {@link ByteBuffer} message, possibly null.   * @return a T value representation of the bytes between the given buffer's current position and   *     its limit, or null, if message is null.   */  @Nullable  T decodeMessage(@Nullable ByteBuffer message);}

最后,MessageHandler 用于接受從 Flutter 傳遞過來的消息。這里簡單的將消息打印出來。

當需要向 Flutter 發送消息時,執行以下代碼即可:

channel?.send("androidcall")

(2)用 使 engine cache 預熱

一般情況我們在 Application 中添加 cache,如下:
class App : Application() {    companion object{        ...        lateinit var flutterEngine2 : FlutterEngine    }    override fun onCreate() {        super.onCreate()        ...
        flutterEngine2 = FlutterEngine(this)        flutterEngine2.navigationChannel.setInitialRoute("second")        flutterEngine2.dartExecutor.executeDartEntrypoint(                DartExecutor.DartEntrypoint.createDefault()        )        FlutterEngineCache.getInstance().put("second", flutterEngine2)    }}

這里我們為 second 這個 Flutter 頁面創建 engine 并加入 cache 進行預熱。

如果我們想使用這個 engine 發送消息,那么可以直接創建 BasicMessageChannel

var channel = BasicMessageChannel<String>(App.flutterEngine2.dartExecutor.binaryMessenger,"test" ,StandardMessageCodec.INSTANCE as MessageCodec<String>)channel.setMessageHandler { message, reply ->    Log.e("recieve", message)}

后續與上面就一樣了。

Flutter 端

步驟基本一樣,先創建
staticconstmessageChannel=constBasicMessageChannel("test",StringCodec());

這里通道名稱保持與 native 一致。

設置回調:

    messageChannel.setMessageHandler((message) async      {        print(message)      }    );

發送消息:

messageChannel.send("flutter call");

這樣就實現了 Native 和 Flutter 的雙向消息交互。

MethodChannel

用于雙方函數的調用,使用方法與 BasicMessageChannel 相似,其實本質上是一樣的。我們先來看看如何使用它。

Android 端

與 BasicMessageChannel 一樣預熱和不預熱可以有兩種不同的處理,但是其實最終都是獲取到 FlutterEngine 對象,所以就不贅述了,直接使用即可。代碼如下:

  //創建varchannel=MethodChannel(flutterEngine.dartExecutor.binaryMessenger,"test")  //回調,根據call執行native函數  channel.setMethodCallHandler { call, result ->      when(call.method){          "flutterCall" -> {//執行我們自定義的對應函數flutterCall(call.arguments)          }          else -> {}      }}

這里 FlutterCall 是響應 Flutter 發送過來的請求,我們定義一個對應的函數來處理,如:

    fun flutterCall(arguments : Object){        Log.e("flutterCall", "message:" + arguments.toString())    }

然后我們可以通過 invokeMethod 函數來執行 Flutter 函數,如:

  //執行flutter函數  channel.invokeMethod("androidCall", "android message")

Flutter 端

流程一樣,代碼如下:

//創建static const methodChannel = const MethodChannel("test");//回調,根據call執行flutter函數    methodChannel.setMethodCallHandler((call) async {      switch(call.method){        case "androidCall":          //執行自定義的對應函數androidCall(call.arguments);          break;      }    });//執行native函數methodChannel.invokeMethod("flutterCall", "flutter message");

源碼分析

在分析 BasicMessageChannel 時我們知道它的 send 函數其實是調用了 messenger.send(...),這個 messenger 是 BinaryMessenger,就是構造函數的第一個參數。MethodCannel 也是一樣,它的 invokeMethod 函數源碼如下:

  @UiThread  public void invokeMethod(String method, @Nullable Object arguments, @Nullable Result callback) {    messenger.send(        name,        codec.encodeMethodCall(new MethodCall(method, arguments)),        callback == null ? null : new IncomingResultHandler(callback));  }

可以看到,最終還是調用了 BinaryMessenger 的 send 函數。只不過將 invokeMethod 的兩個參數 (String 類型的函數名 method 和 Object 類型的參數 arguments) 封裝到 MethodCall 中。

再來看回調的處理,上面 invokeMethod 函數中可以看到,用 IncomingResultHandler 將 callback 進行了封裝,它的關鍵源碼如下:

  private final class IncomingMethodCallHandler implements BinaryMessageHandler {    private final MethodCallHandler handler;
    IncomingMethodCallHandler(MethodCallHandler handler) {      this.handler = handler;    }
    @Override    @UiThread    public void onMessage(ByteBuffer message, final BinaryReply reply) {      final MethodCall call = codec.decodeMethodCall(message);      try {        handler.onMethodCall(            call,            new Result() {              ...            });      } catch (RuntimeException e) {        ...      }}    ...  }

可以看到在收到消息 onMessage 后先將消息解析成 MethodCall 在執行 callback,這樣就可以直接獲取到函數名及參數了。

通過上面我們知道 MethodChannel 和 BasicMessageChannel 本質是一樣的,只不過經過了一層 MethodCall 的封裝,方便直接獲取函數名和參數。

EventChannel

EventChannel 與上面兩個都不太一樣,它是 Flutter 發起,native 處理并返回結果,Flutter 再處理結果。說它是單方向通道也不是很準確,但是 native 無法主動發起,所以更像是一個 c/s 結構。

先來看看如何使用。

Android 端

同樣需要 FlutterEngine 對象,代碼如下:

//創建varchannel=EventChannel(flutterEngine.dartExecutor.binaryMessenger,"test")//設置處理handlerchannel.setStreamHandler(object : StreamHandler(), EventChannel.StreamHandler {    override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {        //根據arguments處理        arguments?.let {...//將處理結果返回,可能成功也可能失敗events?.success("android back")//events?.error("errorcode","errormssage",null)//如果不返回,即success和error都不執行,則需要執行endOfStream//events?.endOfStream()        }    }
overridefunonCancel(arguments:Any?){//執行取消操作    }})

上面提到 Native 無法主動發起,所以就沒有類似上面 send 或 invokeMethod 函數。

Flutter 端

通過 receiveBroadcastStream 來發送 event 請求,并通過 linsten 來監聽返回。

//創建static const eventChannel = const EventChannel("test");//發送arguments給native處理,并監聽結果eventChannel.receiveBroadcastStream(["flutter event"]).listen((event){  //返回成功結果,處理print(event.toString());}, onError: (event){//返回錯誤結果,處理},onDone:(){//執行完成處理});

源碼分析

我們來看一下 receiveBroadcastStream 的關鍵源碼:

  Stream<dynamic> receiveBroadcastStream([ dynamic arguments ]) {    final MethodChannel methodChannel = MethodChannel(name, codec);    late StreamController<dynamic> controller;    controller = StreamController<dynamic>.broadcast(onListen: () async {      binaryMessenger.setMessageHandler(name, (ByteData? reply) async {...      });      try {        await methodChannel.invokeMethod<void>('listen', arguments);      } catch (exception, stack) {        ...      }    }, onCancel: () async {      binaryMessenger.setMessageHandler(name, null);      try {        await methodChannel.invokeMethod<void>('cancel', arguments);      } catch (exception, stack) {        ...      }    });    return controller.stream;  }

可以看到 EventChannel 本質上就是 MethodChannel,只不過執行了幾個預先定義好的函數,如 listen 和 cancel。這樣對 MethodChannel 進行再次封裝,可以更簡單的進行事件傳遞。

總結

上面我們展示了三種交互方式的使用,并解析了其內部的聯系。其實可以看到三種方式最終其實都是使用了 BinaryMessenger 這一抽象類的默認實現 _DefaultBinaryMessenger。所以如果我們通過 BinaryMessenger 來實現一套自己特別的消息傳遞機制。

"開發者說·DTalk" 面向中國開發者們征集 Google 移動應用 (apps & games) 相關的產品/技術內容。歡迎大家前來分享您對移動應用的行業洞察或見解、移動開發過程中的心得或新發現、以及應用出海的實戰經驗總結和相關產品的使用反饋等。我們由衷地希望可以給這些出眾的中國開發者們提供更好展現自己、充分發揮自己特長的平臺。我們將通過大家的技術內容著重選出優秀案例進行谷歌開發技術專家 (GDE) 的推薦。

原文標題:Flutter 如何與 Native (Android) 進行交互 | 開發者說·DTalk

文章出處:【微信公眾號:谷歌開發者】歡迎添加關注!文章轉載請注明出處。

審核編輯:湯梓紅


聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • Android
    +關注

    關注

    12

    文章

    3935

    瀏覽量

    127339
  • 源碼
    +關注

    關注

    8

    文章

    639

    瀏覽量

    29185
  • 交互
    +關注

    關注

    1

    文章

    68

    瀏覽量

    14793

原文標題:Flutter 如何與 Native (Android) 進行交互 | 開發者說·DTalk

文章出處:【微信號:Google_Developers,微信公眾號:谷歌開發者】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    車聯網的三種打開方式

    目前,中國的車聯網發展如何,僅從智能交互角度,如何撬動車聯網的藍海。這里分享了三種流派。
    發表于 11-27 09:48 ?6152次閱讀

    伺服電機的三種控制方式

    伺服電機控制方式有脈沖、模擬量和通訊這三種,在不同的應用場景下,我們該如何選擇伺服電機的控制方式呢?
    發表于 08-17 11:01 ?7096次閱讀

    鴻蒙Flutter實戰:05-使用第方插件

    # 鴻蒙Flutter 實戰:使用第方插件 在鴻蒙Flutter開發中,如果涉及到使用原生功能,就要使用插件。使用插件有兩
    發表于 10-22 21:54

    鴻蒙Flutter實戰:07混合開發

    # 鴻蒙Flutter實戰:混合開發 鴻蒙Flutter混合開發主要有兩形式。 ## 1.基于har 將flutter module打包成har包,在
    發表于 10-23 16:00

    三種復位方式比較

    三種復位方式比較
    發表于 08-16 17:31

    步進電機的三種驅動方式

    步進電機的三種驅動方式
    發表于 01-12 17:03

    請問stm32啟動的三種方式是什么意思?

    請群主詳細解釋下這三種啟動方式,看了參考資料不是很明白其意!謝謝!
    發表于 07-17 04:35

    常見的三種無線接入方式是什么?

    藍牙無線組網的優點是什么?常見的三種無線接入方式是什么?藍牙無線組網原理與上網方案分享
    發表于 05-26 06:33

    STM32三種啟動方式是什么

    STM32三種啟動方式是什么
    發表于 12-15 07:16

    壓供電系統的三種運行方式

    我國低壓供電系統的三種運行方式:國低壓供電系統主要有三種運行方式:TN系統、TT系統、lT系統。
    發表于 05-26 17:06 ?1.1w次閱讀
    壓供電系統的<b class='flag-5'>三種</b>運行<b class='flag-5'>方式</b>

    伺服電機的三種控制方式該如何應用

    一般伺服都有三種控制方式:速度控制方式,轉矩控制方式,位置控制方式。大多數人想知道的就是這三種
    的頭像 發表于 12-14 23:12 ?5350次閱讀

    如何應用伺服電機的三種控制方式

    一般伺服都有三種控制方式:速度控制方式,轉矩控制方式,位置控制方式。大多數人想知道的就是這三種
    發表于 01-22 06:30 ?7次下載
    如何應用伺服電機的<b class='flag-5'>三種</b>控制<b class='flag-5'>方式</b>

    原生開發如何學習Flutter

    原生 Android/ iOS 開發介紹如何正確學習和使用 Flutter
    的頭像 發表于 02-18 18:43 ?1730次閱讀

    縮放模擬輸入信號的三種方式

    縮放模擬輸入信號的三種方式
    發表于 11-02 08:16 ?1次下載
    縮放模擬輸入信號的<b class='flag-5'>三種</b><b class='flag-5'>方式</b>

    Redis實現限流的三種方式分享

    當然,限流有許多種實現的方式,Redis具有很強大的功能,我用Redis實踐了三種的實現方式,可以較為簡單的實現其方式。
    的頭像 發表于 02-22 09:52 ?1072次閱讀
    主站蜘蛛池模板: 神马电影院午 夜理论| 樱桃视频高清免费观看在线播放| 伊人久久精品午夜| 国产超碰精久久久久久无码AV| 麻豆一二三区果冻| 性xxxx直播放免费| 成人免费观看在线视频| 久久vs国产综合色| 小夫妻天天恶战| 成人在线观看视频免费| 免费无码国产欧美久久18| 亚洲色欲H网在线观看| 国产精品久久久久久人妻精品蜜桃 | 亚洲AV无码专区国产精品麻豆| oldgrand欧洲老妇人| 伦理片飘花手机在线| 亚洲视频无码中字在线| 国产精品视频国产永久视频| 日本漫画无彩翼漫画| 99re热精品视频国产免费| 久久精品黄色| 亚洲色欲国产免费视频| 国产乱人视频在线观看| 舔1V1高H糙汉| 俄罗斯videosbest8| 日本高清免费看| 99久久久精品免费观看国产| 快播电影频道| 中国特级黄色大片| 久久99精品国产99久久6男男| 亚洲精品国产精品麻豆99| 国产精品久久久久永久免费看| 色戒床震视频片段| 成年人深夜福利| 强开乳罩摸双乳吃奶视频| a免费视频| 欧美多人群p刺激交换电影| 99日韩精品| 欧美日韩北条麻妃一区二区| 99在线这精品视频| 女人高潮了拔出来了她什么感觉|