Flutter框架分析(八)-Platform Channel

1. 前言

因爲Flutter運行於其餘平臺之上,其不可避免的須要和宿主平臺進行通訊。那其Flutter和宿主平臺是如何進行通訊的呢?本文將詳細介紹Flutter和宿主平臺的通訊方式,並從源碼的角度剖析通訊原理。android

2. 架構概述

Flutter是經過Platform Channel同宿主平臺進行通訊的。其消息通道結構示意圖以下:數據庫

image.jpeg

爲了保證界面可以響應及時,消息的傳遞是異步的。
Flutter定義了三種不一樣類型的Platform Channel,它們分別是:數組

  • BasicMessageChannel:用於傳遞字符串和半結構化的信息。支持數據雙向傳遞,有返回值。markdown

  • MethodChannel:用於傳遞方法調用(method invocation)。支持數據雙向傳遞,有返回值。架構

  • EventChannel: 用於數據流(event streams)的通訊,僅支持數據單向傳遞,無返回值。框架

三種Channel之間互相獨立,各有用途,但它們在設計上卻很是相近。在宿主平臺側,其設計也很是相似。如下是FlutterAndroid平臺上的Platform Channel結構示意圖。異步

image.jpeg

消息的數據載體是ByteBuffer,在AndroidFlutter側都是經過BinaryMessager來發送和接收數據。接下來,會分別對Android端的三種Channel,以及Flutter端的三種Channel源碼進行分析。async

3. Android端結構

AndroidPlatform Channel結構示意圖以下。ide

image.jpeg

如圖所示,BinaryMessager不會直接和Channel通訊,而是經過Channel中的接口BinaryMessageHandler進行通訊。在BinaryMessageHandler中,真正處理消息的是:BasicMessageChannel中的MessageHandlerMethodChannel中的MethodCallHandler,以及EventChannel中的StreamHandler。因爲Android端沒法之間使用二進制數據(ByteBuffer),所以在上述Handler在收發消息以前,會經過對應的Codec進行加解密,用於完成ByteBuffer和所需數據格式之間的轉換。函數

當咱們使用一個Channel時,咱們須要初始化某個名字(Channel Name)的Channel,而後向該Channel註冊一個處理消息的Handler。此時,系統會自動生成一個與之對應的BinaryMessageHandler,並以Channel Namekey,將其註冊至BinaryMessager中。其源碼以下:

class DartMessenger implements BinaryMessengerPlatformMessageHandler {
  //處理消息的handler
  @NonNull private final Map<String, BinaryMessenger.BinaryMessageHandler> messageHandlers;

  DartMessenger(@NonNull FlutterJNI flutterJNI) {
    this.messageHandlers = new HashMap<>();
  }

  @Override
  public void setMessageHandler(
      @NonNull String channel, @Nullable BinaryMessenger.BinaryMessageHandler handler) {
    if (handler == null) {
      Log.*v*(*TAG*, "Removing handler for channel '" + channel + "'");
      messageHandlers.remove(channel);
    } else {
      Log.*v*(*TAG*, "Setting handler for channel '" + channel + "'");
      messageHandlers.put(channel, handler);
    }
  }
 }

複製代碼

能夠看到,各個Channel對應的消息處理Handler均以Map的形式存儲在BinaryMessager中,Map的key是Channel Name

3.1 BasicMessageChannel

用於傳遞字符串和半結構化的信息。支持數據雙向傳遞,有返回值。其對應的消息處理HandlerMessageHandler,對應的CodecMessageCodecMessageHandler的onMessage方法接收一個T類型的消息,並異步返回一個相同類型result。MessageCodec主要用於二進制格式數據(ByteBuffer)與基礎數據之間的編解碼。

其定義了兩個方法:

  • encodeMessage:接收特定數據類型,並將其編碼爲二進制數據ByteBuffer

  • decodeMessage:接收二進制數據ByteBuffer,並將其解碼爲特定數據類型。

MessageCodec有如下幾種:

codec類 特色
StandardMessageCodec(默認編碼器) 支持基礎數據類型、二進制數據、列表、字典。
BinaryCodec 1. 無編解碼,返回值和入參類型都是二進制格式。2. 可以使傳遞內存數據庫時在編解碼階段免於內存拷貝。
StringCodec 字符串與二進制數據之間的編解碼。
JSONMessageCodec 1. 基礎數據與二進制數據之間的編解碼。2. 支持基礎數據類型、列表、字典。

3.2 MethodChannel

MethodChannel用於傳遞方法調用(method invocation)。支持數據雙向傳遞,有返回值。其對應的消息處理HandlerMethodCallHandler,對應的CodecMethodCodecMethodCallHandler的onMessage方法接收一個MethodCall類型消息,並根據MethodCall的成員變量method去調用對應的API,當處理完成後,根據方法調用成功或失敗,返回對應的結果。MethodCodec用於二進制數據與方法調用(MethodCall)和返回結果之間的編解碼。其定義了五個方法:

  • encodeMethodCall:將方法調用消息加密爲二進制數據。

  • decodeMethodCall:將二進制數據解密爲方法調用。

  • encodeSuccessEnvelope:將成功的方法調用結果加密爲二進制數據。

  • encodeErrorEnvelope:將失敗的方法調用結果加密爲二進制數據。

  • decodeEnvelope:將二進制數據解密爲方法調用結果。

MethodCodec有如下兩類:

codec類 特色
StandardMethodCodec(默認編碼器) 依賴於StandardMessageCodec,當其編碼MethodCall時,會將method和args依次使用StandardMessageCodec編碼,寫入二進制數據容器。在編碼方法的調用結果時:若是調用成功,會先向二進制數據容器寫入數值0(表明調用成功),再寫入StandardMessageCodec編碼後的result。若是調用失敗,會先向二進制數據容器寫入數值1(表明調用失敗),再寫入StandardMessageCodec編碼後的code,message和detail。
JSONMethodCodec 依賴於JSONMessageCodec. 編碼MethodCall時,會將其轉化爲JSON串{「method」:method, 「args」:args}。 在編碼調用結果時,會將其轉化爲一個數組:調用成功爲[result], 調用失敗爲[code, message, detail]。再使用JSONMessageCodec將JSON串或數組轉化爲二進制數據。

3.3 EventChannel

EventChannel用於數據流(event streams)的通訊,僅支持數據單向傳遞(從PlatformFlutter),無返回值。其對應的消息處理HandlerStreamHandler,對應的Codec也是MethodCodecStreamHandler與前二者稍顯不一樣,用於事件流的通訊,最爲常見的用途就是Platform端向Flutter端發送事件消息。當咱們實現一個StreamHandler時,須要實現其onListen和onCancel方法。而在onListen方法的入參中,有一個EventSink(其在Android是一個對象,iOS端則是一個block)。咱們持有EventSink後,便可經過EventSinkFlutter端發送事件消息。其流程圖以下:

image.jpeg

StreamHandler工做原理並不複雜。在使用StreamHandler時,首先咱們須要在Android端使用一個channel name初始化一個的EventChannel,而後再設置該EventChannelStreamHandler,在設置該StreamHandler的時候,會以channel name爲key將包含該StreamHandlerBinaryMessageHandler註冊到BinaryMessager中,這樣BinaryMessager在收發消息的時候,就能夠經過channel name找到對應的StreamHandler,而後進行處理。

而後在Flutter端,咱們須要使用同一個channel name初始化一個EventChannel,而後註冊其監聽。在註冊監聽後,Flutter會發送一個二進制消息到Platform端。Platform端用MethodCodec將該消息解碼爲MethodCall,若是MethodCall的method的值爲"listen",則調用StreamHandler的onListen方法,傳遞給StreamHandler一個EventSink。而經過EventSinkFlutter端發送消息時,實際上就是經過BinaryMessager的send方法將消息傳遞過去。

EventChannel使用的Codec也是MethodCodec,此處就再也不重複了。

4. Flutter端結構

FlutterPlatform Channel結構示意圖以下。

image.jpeg

Android端相似,BinaryMessager不會直接和Channel通訊,而是經過Channel中的接口MessageHandler進行通訊。在MessageHandler中,真正處理消息的是咱們重寫的handler函數。因爲Flutter端沒法之間使用二進制數據(ByteBuffer),所以在上述Handler在收發消息以前,會經過對應的Codec進行加解密,用於完成ByteBuffer和所需數據格式之間的轉換。

當咱們使用一個Channel時,咱們須要初始化某個名字(Channel Name)的Channel,而後向該Channel註冊一個處理消息的handler。此時,系統會自動生成一個與之對應的MessageHandler,並以Channel Name爲key,將其註冊至BinaryMessager中。其源碼以下:

class _DefaultBinaryMessenger extends BinaryMessenger {
   static final Map<String, MessageHandler>  _handlers =
      <String, MessageHandler>{};

 

   @override
   void setMessageHandler(String channel, MessageHandler handler) {
      if (handler == null)
         _handlers.remove(channel);
      else
         _handlers[channel] = handler;
    ui.channelBuffers.drain(channel(ByteData data, ui.PlatformMessageResponseCall back callback) async {
    await handlePlatformMessage(channel, data, callback);
      });
   }
}
複製代碼

能夠看到,和Android端同樣,各個Channel對應的消息處理Handler均以Map的形式存儲在BinaryMessager中,Map的key是Channel Name。

4.1 BasicMessageChannel

BasicMessageChannel用於傳遞字符串和半結構化的信息,對應Android端的BasicMessageChannel。其對應的CodecMessageCodecMessageCodec主要用於二進制格式數據(ByteData)與基礎數據之間的編解碼。其實現子類及該子類支持的數據格式同AndroidMessageCodec一一對應。

4.2 MethodChannel

MethodChannel用於傳遞方法調用(method invocation),對應Android端的MethodChannel。其對應的CodecMethodCodecMethodCodec主要用於二進制格式數據(ByteData)與消息調用及結果之間的編解碼。其實現子類及該子類支持的數據格式同AndroidMethodCodec一一對應。

4.3 EventChannel

EventChannel用於數據流(event streams)的通訊,對應Android端的EventChannel。其對應的Codec也是MethodCodec

5. 消息傳遞流程

接下來,以一次MethodChannel爲例,咱們經過源碼來理解消息的傳遞過程。

5.1 Dart層

當咱們在Flutter端使用MethodChannel的invokeMethod方法發起一次方法調用時,就開始了咱們的消息傳遞之旅。首先,invokeMethod方法會將message和arguments封裝成一個MethodCall對象,而後經過MethodCodec的encodeMethodCall函數將其編碼成二進制數據(ByteData),最後經過BinaryMessages將其發送至Engine中的native層。

負責將調用傳遞給native層的方法是_sendPlatformMessage,該方法是一個native方法,其有三個參數:

  • name,String類型,值爲channel name

  • callback,PlatformMessageResponseCallback類型,值爲回調函數

  • data,ByteData類型,即二進制類型,即編碼過的二進制數據

5.2 native層

_sendPlatformMessage在native層中對應的函數是platform_configuration.cc文件中的_sendPlatformMessage函數。此對應關係是在Dart虛擬機建立時創建的,函數調用流程圖以下:

image.jpeg

其實很好理解,該函數映射表做爲dart層和native層之間的通訊通道,屬於Flutter Engine的基礎,必須在初始階段建立,和Java中jni的函數映射表相似。

_sendPlatformMessage函數接收了來自dart層的參數,並對其進行了封裝:

callback被封裝成native的回調PlatformMessageResponse類型的response;data被轉化爲uint8_t*類型的buffer;而後根據name,data,和buffer生成PlatformMessage類型的消息,並傳遞給RuntimeControllerHandlePlatformMessage進行處理。

RuntimeController會將消息交給其代理RuntimeDelegate處理。RuntimeDelegate的實現類是EngineEngine在處理消息時,會交由其代理Engine::Delegate處理,而該代理的實際實現爲Shell,其OnEngineHandlePlatformMessage接收到消息後,會向PlatformTaskRunner添加一個Task,該Task會調用PlatformViewHandlePlatformMessage方法。

platform_view_android是Platformview的子類,也是其在Android端的具體實現。當platform_view_android接收到PlatformMessage類型的消息時,若是消息中有response(類型爲PlatformMessageResponse),則生成一個自增加的response_id,並以response_id爲key,response爲value存入字典pending_responses_中。接着,將channel和data均轉化爲Java可識別的數據,經過jni向Java層發起調用,將response_id、channel和data傳遞過去。

其流程圖以下。

image.jpeg

5.3 Java層

Java和native的接口類是FlutterJNIAndroidFlutter Engine全部交互的jni接口均在該類中定義。在FlutterJNI中,負責接收PlatformMessage類型的消息的函數是handlePlatformMessage,該函數會將接收到的PlatformMessage交給BinaryMessager處理。在BinaryMessager中,由上文可知保存着以channel爲key的BinaryMessageHandler。所以能夠根據native傳遞過來的channel找到對應的BinaryMessageHandler,而後將data轉化爲二進制數據並交由該BinaryMessageHandler處理。

BinaryMessageHandler處理完成後,FlutterJNI會經過jni調用native的方法,將responseId和message傳遞到native層。native層,platform_view_android的InvokePlatformMessageResponseCallback接收到了responseId和message。其先將message轉化爲二進制結果,並根據responseId,從panding_responses_中找到對應的PlatformMessageResponse對象,調用其Complete方法將二進制結果返回。PlatformMessageResponseComplete方法向UI Task Runner添加了一個新的Task,這個Task的做用是將二進制結果從native的二進制數據類型轉化爲Dart的二進制數據類型response,並調用dart的callback將response傳遞到Dart層。

Dart層接收到二進制數據後,使用MethodCodec將數據解碼,並返回給業務層。至此,一次從Flutter發起的方法調用就完整結束了。

上述過程的流程圖以下:

image.jpeg

6. 小結

本文主要介紹了Platform Channel的主要類型,並分析了其在Android端和Flutter端的主要結構。最後經過方法的調用過程,分析了消息的具體傳遞流程。

7. 參考文檔

一篇看懂Android與Flutter之間的通訊
深刻理解Flutter Platform Channel
Android Flutter:手把手教你如何進行Android 與 Flutter的相互通訊

8. 相關文章

Flutter框架分析(一)--架構總覽
Flutter框架分析(二)-- Widget
Flutter框架分析(三)-- Element
Flutter框架分析(四)-RenderObject
Flutter框架分析(五)-Widget,Element,RenderObject樹
Flutter框架分析(六)-Constraint
Flutter框架分析(七)-relayoutBoundary
Flutter框架分析- Parent Data
Flutter框架分析 -InheritedWidget

相關文章
相關標籤/搜索