在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

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

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

之前我們介紹了如何在 Native (Android 項(xiàng)目) 中啟動(dòng) Flutter,展示 Flutter 頁面。但是在開發(fā)過程中,很多時(shí)候并不是簡(jiǎn)單的展示一個(gè)頁面即可,還會(huì)涉及到各種交互,比如傳遞一些消息。

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

1.BasicMessageChannel

2.MethodChannel

3.EventChannel

BasicMessageChannel

雖然說是三種交互方式,但是其實(shí)本質(zhì)都是一種,這個(gè)我們后面會(huì)解釋。

先來看看 BasicMessageChannel。它可以實(shí)現(xiàn)雙方交互,發(fā)送一些簡(jiǎn)單消息,消息類型 Object,但是并不是所有 Object 都可以,基礎(chǔ)類型及基礎(chǔ)類型的數(shù)組、list、map 是可以的。這個(gè)可以參考 BasicMessageChannel 的源碼:

  public void send(@Nullable T message, @Nullable final Reply callback) {    messenger.send(        name,        codec.encodeMessage(message),        callback == null ? null : new IncomingReplyHandler(callback));  }
可以看到進(jìn)行了 encode,這個(gè) codec 一般是 StandardMessageCodec,它的 encodeMessage 函數(shù)源碼:
  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 預(yù)熱

如果不使用 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)        }    }}

注意這里第二個(gè)參數(shù) "test" 是這通道 (channel) 的名稱,兩邊名稱一致才能進(jìn)行通信。

第三個(gè)參數(shù)是消息的編解碼器,這里我們因?yàn)槭呛?jiǎn)單的示例,消息是字符串 String,所以用 StringCodec。

StringCodec 是 MessageCodec 接口的實(shí)現(xiàn),除了它還有 BinaryCodec,JsonMessageCodec,StandardMessageCodec。另外我們還可以自己實(shí)現(xiàn) MessageCodec,實(shí)現(xiàn)它的兩個(gè)函數(shù)即可,它的源碼如下:

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 傳遞過來的消息。這里簡(jiǎn)單的將消息打印出來。

當(dāng)需要向 Flutter 發(fā)送消息時(shí),執(zhí)行以下代碼即可:

channel?.send("androidcall")

(2)用 使 engine cache 預(yù)熱

一般情況我們?cè)?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)    }}

這里我們?yōu)?second 這個(gè) Flutter 頁面創(chuàng)建 engine 并加入 cache 進(jìn)行預(yù)熱。

如果我們想使用這個(gè) engine 發(fā)送消息,那么可以直接創(chuàng)建 BasicMessageChannel

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

后續(xù)與上面就一樣了。

Flutter 端

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

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

設(shè)置回調(diào):

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

發(fā)送消息:

messageChannel.send("flutter call");

這樣就實(shí)現(xiàn)了 Native 和 Flutter 的雙向消息交互。

MethodChannel

用于雙方函數(shù)的調(diào)用,使用方法與 BasicMessageChannel 相似,其實(shí)本質(zhì)上是一樣的。我們先來看看如何使用它。

Android 端

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

  //創(chuàng)建varchannel=MethodChannel(flutterEngine.dartExecutor.binaryMessenger,"test")  //回調(diào),根據(jù)call執(zhí)行native函數(shù)  channel.setMethodCallHandler { call, result ->      when(call.method){          "flutterCall" -> {//執(zhí)行我們自定義的對(duì)應(yīng)函數(shù)flutterCall(call.arguments)          }          else -> {}      }}

這里 FlutterCall 是響應(yīng) Flutter 發(fā)送過來的請(qǐng)求,我們定義一個(gè)對(duì)應(yīng)的函數(shù)來處理,如:

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

然后我們可以通過 invokeMethod 函數(shù)來執(zhí)行 Flutter 函數(shù),如:

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

Flutter 端

流程一樣,代碼如下:

//創(chuàng)建static const methodChannel = const MethodChannel("test");//回調(diào),根據(jù)call執(zhí)行flutter函數(shù)    methodChannel.setMethodCallHandler((call) async {      switch(call.method){        case "androidCall":          //執(zhí)行自定義的對(duì)應(yīng)函數(shù)androidCall(call.arguments);          break;      }    });//執(zhí)行native函數(shù)methodChannel.invokeMethod("flutterCall", "flutter message");

源碼分析

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

  @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));  }

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

再來看回調(diào)的處理,上面 invokeMethod 函數(shù)中可以看到,用 IncomingResultHandler 將 callback 進(jìn)行了封裝,它的關(guān)鍵源碼如下:

  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 在執(zhí)行 callback,這樣就可以直接獲取到函數(shù)名及參數(shù)了。

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

EventChannel

EventChannel 與上面兩個(gè)都不太一樣,它是 Flutter 發(fā)起,native 處理并返回結(jié)果,F(xiàn)lutter 再處理結(jié)果。說它是單方向通道也不是很準(zhǔn)確,但是 native 無法主動(dòng)發(fā)起,所以更像是一個(gè) c/s 結(jié)構(gòu)。

先來看看如何使用。

Android 端

同樣需要 FlutterEngine 對(duì)象,代碼如下:

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

上面提到 Native 無法主動(dòng)發(fā)起,所以就沒有類似上面 send 或 invokeMethod 函數(shù)。

Flutter 端

通過 receiveBroadcastStream 來發(fā)送 event 請(qǐng)求,并通過 linsten 來監(jiān)聽返回。

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

源碼分析

我們來看一下 receiveBroadcastStream 的關(guān)鍵源碼:

  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 本質(zhì)上就是 MethodChannel,只不過執(zhí)行了幾個(gè)預(yù)先定義好的函數(shù),如 listen 和 cancel。這樣對(duì) MethodChannel 進(jìn)行再次封裝,可以更簡(jiǎn)單的進(jìn)行事件傳遞。

總結(jié)

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

"開發(fā)者說·DTalk" 面向中國(guó)開發(fā)者們征集 Google 移動(dòng)應(yīng)用 (apps & games) 相關(guān)的產(chǎn)品/技術(shù)內(nèi)容。歡迎大家前來分享您對(duì)移動(dòng)應(yīng)用的行業(yè)洞察或見解、移動(dòng)開發(fā)過程中的心得或新發(fā)現(xiàn)、以及應(yīng)用出海的實(shí)戰(zhàn)經(jīng)驗(yàn)總結(jié)和相關(guān)產(chǎn)品的使用反饋等。我們由衷地希望可以給這些出眾的中國(guó)開發(fā)者們提供更好展現(xiàn)自己、充分發(fā)揮自己特長(zhǎng)的平臺(tái)。我們將通過大家的技術(shù)內(nèi)容著重選出優(yōu)秀案例進(jìn)行谷歌開發(fā)技術(shù)專家 (GDE) 的推薦

原文標(biāo)題:Flutter 如何與 Native (Android) 進(jìn)行交互 | 開發(fā)者說·DTalk

文章出處:【微信公眾號(hào):谷歌開發(fā)者】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

審核編輯:湯梓紅


聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • Android
    +關(guān)注

    關(guān)注

    12

    文章

    3945

    瀏覽量

    127931
  • 源碼
    +關(guān)注

    關(guān)注

    8

    文章

    652

    瀏覽量

    29452
  • 交互
    +關(guān)注

    關(guān)注

    1

    文章

    69

    瀏覽量

    14831

原文標(biāo)題:Flutter 如何與 Native (Android) 進(jìn)行交互 | 開發(fā)者說·DTalk

文章出處:【微信號(hào):Google_Developers,微信公眾號(hào):谷歌開發(fā)者】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    車聯(lián)網(wǎng)的三種打開方式

    目前,中國(guó)的車聯(lián)網(wǎng)發(fā)展如何,僅從智能交互角度,如何撬動(dòng)車聯(lián)網(wǎng)的藍(lán)海。這里分享了三種流派。
    發(fā)表于 11-27 09:48 ?6179次閱讀

    伺服電機(jī)的三種控制方式

    伺服電機(jī)控制方式有脈沖、模擬量和通訊這三種,在不同的應(yīng)用場(chǎng)景下,我們?cè)撊绾芜x擇伺服電機(jī)的控制方式呢?
    發(fā)表于 08-17 11:01 ?7160次閱讀

    鴻蒙Flutter實(shí)戰(zhàn):05-使用第方插件

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

    鴻蒙Flutter實(shí)戰(zhàn):07混合開發(fā)

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

    三種復(fù)位方式比較

    三種復(fù)位方式比較
    發(fā)表于 08-16 17:31

    步進(jìn)電機(jī)的三種驅(qū)動(dòng)方式

    步進(jìn)電機(jī)的三種驅(qū)動(dòng)方式
    發(fā)表于 01-12 17:03

    請(qǐng)問stm32啟動(dòng)的三種方式是什么意思?

    請(qǐng)群主詳細(xì)解釋下這三種啟動(dòng)方式,看了參考資料不是很明白其意!謝謝!
    發(fā)表于 07-17 04:35

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

    藍(lán)牙無線組網(wǎng)的優(yōu)點(diǎn)是什么?常見的三種無線接入方式是什么?藍(lán)牙無線組網(wǎng)原理與上網(wǎng)方案分享
    發(fā)表于 05-26 06:33

    STM32三種啟動(dòng)方式是什么

    STM32三種啟動(dòng)方式是什么
    發(fā)表于 12-15 07:16

    壓供電系統(tǒng)的三種運(yùn)行方式

    我國(guó)低壓供電系統(tǒng)的三種運(yùn)行方式:國(guó)低壓供電系統(tǒng)主要有三種運(yùn)行方式:TN系統(tǒng)、TT系統(tǒng)、lT系統(tǒng)。
    發(fā)表于 05-26 17:06 ?1.1w次閱讀
    壓供電系統(tǒng)的<b class='flag-5'>三種</b>運(yùn)行<b class='flag-5'>方式</b>

    伺服電機(jī)的三種控制方式該如何應(yīng)用

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

    如何應(yīng)用伺服電機(jī)的三種控制方式

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

    原生開發(fā)如何學(xué)習(xí)Flutter

    原生 Android/ iOS 開發(fā)介紹如何正確學(xué)習(xí)和使用 Flutter
    的頭像 發(fā)表于 02-18 18:43 ?1776次閱讀

    縮放模擬輸入信號(hào)的三種方式

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

    Redis實(shí)現(xiàn)限流的三種方式分享

    當(dāng)然,限流有許多種實(shí)現(xiàn)的方式,Redis具有很強(qiáng)大的功能,我用Redis實(shí)踐了三種的實(shí)現(xiàn)方式,可以較為簡(jiǎn)單的實(shí)現(xiàn)其方式
    的頭像 發(fā)表于 02-22 09:52 ?1134次閱讀
    主站蜘蛛池模板: 99久久精品费精品国产一区二 | 中文字幕一区二区三区5566 | 天堂影 | 多男一女一级淫片免费播放口 | 岛国中文字幕 | 日本加勒比在线精品视频 | 成在线人视频免费视频 | 教官的好爽好深h片段 | 国产精品美女免费视频观看 | 日本三级日产三级国产三级 | 亚洲国产午夜精品理论片的软件 | 拍拍拍交性免费视频 | 久久久久免费 | 日韩欧美中文字幕在线播放 | 日本一卡二卡3卡四卡网站精品 | 亚洲午夜精品久久久久久成年 | 亚洲第一综合 | 加勒比一本大道香蕉在线视频 | 午夜国产精品久久久久 | 午夜精品在线 | 伊人精品成人久久综合欧美 | 色多多视频官网 | 视频一区二区在线播放 | 在线视频黄色 | 久久精品久久久 | 国产高清在线免费 | 午夜香蕉网| 免费网站看av片 | 狠狠操天天干 | 久久伊人精品青青草原高清 | 奇米社区 | 农村妇女色又黄一级毛片卡 | 色视频一区二区三区 | 色老二精品视频在线观看 | 一区二区三区四区国产精品 | 日本福利小视频 | 午夜污片 | 韩日毛片 | 97影院3 | 狠狠躁夜夜躁人人爽天天段 | 福利片午夜 |