因爲Flutter運行於其餘平臺之上,其不可避免的須要和宿主平臺進行通訊。那其Flutter和宿主平臺是如何進行通訊的呢?本文將詳細介紹Flutter和宿主平臺的通訊方式,並從源碼的角度剖析通訊原理。android
Flutter是經過Platform Channel同宿主平臺進行通訊的。其消息通道結構示意圖以下:數據庫
爲了保證界面可以響應及時,消息的傳遞是異步的。
Flutter定義了三種不一樣類型的Platform Channel,它們分別是:數組
BasicMessageChannel:用於傳遞字符串和半結構化的信息。支持數據雙向傳遞,有返回值。markdown
MethodChannel:用於傳遞方法調用(method invocation)。支持數據雙向傳遞,有返回值。架構
EventChannel: 用於數據流(event streams)的通訊,僅支持數據單向傳遞,無返回值。框架
三種Channel之間互相獨立,各有用途,但它們在設計上卻很是相近。在宿主平臺側,其設計也很是相似。如下是Flutter在Android平臺上的Platform Channel結構示意圖。異步
消息的數據載體是ByteBuffer,在Android和Flutter側都是經過BinaryMessager來發送和接收數據。接下來,會分別對Android端的三種Channel,以及Flutter端的三種Channel源碼進行分析。async
Android端Platform Channel結構示意圖以下。ide
如圖所示,BinaryMessager不會直接和Channel通訊,而是經過Channel中的接口BinaryMessageHandler進行通訊。在BinaryMessageHandler中,真正處理消息的是:BasicMessageChannel中的MessageHandler,MethodChannel中的MethodCallHandler,以及EventChannel中的StreamHandler。因爲Android端沒法之間使用二進制數據(ByteBuffer),所以在上述Handler在收發消息以前,會經過對應的Codec進行加解密,用於完成ByteBuffer和所需數據格式之間的轉換。函數
當咱們使用一個Channel時,咱們須要初始化某個名字(Channel Name)的Channel,而後向該Channel註冊一個處理消息的Handler。此時,系統會自動生成一個與之對應的BinaryMessageHandler,並以Channel Name爲key,將其註冊至BinaryMessager中。其源碼以下:
class DartMessenger implements BinaryMessenger, PlatformMessageHandler {
//處理消息的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。
用於傳遞字符串和半結構化的信息。支持數據雙向傳遞,有返回值。其對應的消息處理Handler是MessageHandler,對應的Codec是MessageCodec。MessageHandler的onMessage方法接收一個T類型的消息,並異步返回一個相同類型result。MessageCodec主要用於二進制格式數據(ByteBuffer)與基礎數據之間的編解碼。
其定義了兩個方法:
encodeMessage:接收特定數據類型,並將其編碼爲二進制數據ByteBuffer。
decodeMessage:接收二進制數據ByteBuffer,並將其解碼爲特定數據類型。
MessageCodec有如下幾種:
codec類 | 特色 |
---|---|
StandardMessageCodec(默認編碼器) | 支持基礎數據類型、二進制數據、列表、字典。 |
BinaryCodec | 1. 無編解碼,返回值和入參類型都是二進制格式。2. 可以使傳遞內存數據庫時在編解碼階段免於內存拷貝。 |
StringCodec | 字符串與二進制數據之間的編解碼。 |
JSONMessageCodec | 1. 基礎數據與二進制數據之間的編解碼。2. 支持基礎數據類型、列表、字典。 |
MethodChannel用於傳遞方法調用(method invocation)。支持數據雙向傳遞,有返回值。其對應的消息處理Handler是MethodCallHandler,對應的Codec是MethodCodec。MethodCallHandler的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串或數組轉化爲二進制數據。 |
EventChannel用於數據流(event streams)的通訊,僅支持數據單向傳遞(從Platform到Flutter),無返回值。其對應的消息處理Handler是StreamHandler,對應的Codec也是MethodCodec。StreamHandler與前二者稍顯不一樣,用於事件流的通訊,最爲常見的用途就是Platform端向Flutter端發送事件消息。當咱們實現一個StreamHandler時,須要實現其onListen和onCancel方法。而在onListen方法的入參中,有一個EventSink(其在Android是一個對象,iOS端則是一個block)。咱們持有EventSink後,便可經過EventSink向Flutter端發送事件消息。其流程圖以下:
StreamHandler工做原理並不複雜。在使用StreamHandler時,首先咱們須要在Android端使用一個channel name初始化一個的EventChannel,而後再設置該EventChannel的StreamHandler,在設置該StreamHandler的時候,會以channel name爲key將包含該StreamHandler的BinaryMessageHandler註冊到BinaryMessager中,這樣BinaryMessager在收發消息的時候,就能夠經過channel name找到對應的StreamHandler,而後進行處理。
而後在Flutter端,咱們須要使用同一個channel name初始化一個EventChannel,而後註冊其監聽。在註冊監聽後,Flutter會發送一個二進制消息到Platform端。Platform端用MethodCodec將該消息解碼爲MethodCall,若是MethodCall的method的值爲"listen",則調用StreamHandler的onListen方法,傳遞給StreamHandler一個EventSink。而經過EventSink向Flutter端發送消息時,實際上就是經過BinaryMessager的send方法將消息傳遞過去。
EventChannel使用的Codec也是MethodCodec,此處就再也不重複了。
Flutter端Platform Channel結構示意圖以下。
和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。
BasicMessageChannel用於傳遞字符串和半結構化的信息,對應Android端的BasicMessageChannel。其對應的Codec是MessageCodec。MessageCodec主要用於二進制格式數據(ByteData)與基礎數據之間的編解碼。其實現子類及該子類支持的數據格式同Android端MessageCodec一一對應。
MethodChannel用於傳遞方法調用(method invocation),對應Android端的MethodChannel。其對應的Codec是MethodCodec。MethodCodec主要用於二進制格式數據(ByteData)與消息調用及結果之間的編解碼。其實現子類及該子類支持的數據格式同Android端MethodCodec一一對應。
EventChannel用於數據流(event streams)的通訊,對應Android端的EventChannel。其對應的Codec也是MethodCodec。
接下來,以一次MethodChannel爲例,咱們經過源碼來理解消息的傳遞過程。
當咱們在Flutter端使用MethodChannel的invokeMethod方法發起一次方法調用時,就開始了咱們的消息傳遞之旅。首先,invokeMethod方法會將message和arguments封裝成一個MethodCall對象,而後經過MethodCodec的encodeMethodCall函數將其編碼成二進制數據(ByteData),最後經過BinaryMessages將其發送至Engine中的native層。
負責將調用傳遞給native層的方法是_sendPlatformMessage,該方法是一個native方法,其有三個參數:
name,String類型,值爲channel name
callback,PlatformMessageResponseCallback類型,值爲回調函數
data,ByteData類型,即二進制類型,即編碼過的二進制數據
_sendPlatformMessage在native層中對應的函數是platform_configuration.cc文件中的_sendPlatformMessage函數。此對應關係是在Dart虛擬機建立時創建的,函數調用流程圖以下:
其實很好理解,該函數映射表做爲dart層和native層之間的通訊通道,屬於Flutter Engine的基礎,必須在初始階段建立,和Java中jni的函數映射表相似。
_sendPlatformMessage函數接收了來自dart層的參數,並對其進行了封裝:
callback被封裝成native的回調PlatformMessageResponse類型的response;data被轉化爲uint8_t*類型的buffer;而後根據name,data,和buffer生成PlatformMessage類型的消息,並傳遞給RuntimeController的HandlePlatformMessage進行處理。
RuntimeController會將消息交給其代理RuntimeDelegate處理。RuntimeDelegate的實現類是Engine,Engine在處理消息時,會交由其代理Engine::Delegate處理,而該代理的實際實現爲Shell,其OnEngineHandlePlatformMessage接收到消息後,會向PlatformTaskRunner添加一個Task,該Task會調用PlatformView的HandlePlatformMessage方法。
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傳遞過去。
其流程圖以下。
Java和native的接口類是FlutterJNI,Android和Flutter 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方法將二進制結果返回。PlatformMessageResponse的Complete方法向UI Task Runner添加了一個新的Task,這個Task的做用是將二進制結果從native的二進制數據類型轉化爲Dart的二進制數據類型response,並調用dart的callback將response傳遞到Dart層。
Dart層接收到二進制數據後,使用MethodCodec將數據解碼,並返回給業務層。至此,一次從Flutter發起的方法調用就完整結束了。
上述過程的流程圖以下:
本文主要介紹了Platform Channel的主要類型,並分析了其在Android端和Flutter端的主要結構。最後經過方法的調用過程,分析了消息的具體傳遞流程。
一篇看懂Android與Flutter之間的通訊
深刻理解Flutter Platform Channel
Android Flutter:手把手教你如何進行Android 與 Flutter的相互通訊
Flutter框架分析(一)--架構總覽
Flutter框架分析(二)-- Widget
Flutter框架分析(三)-- Element
Flutter框架分析(四)-RenderObject
Flutter框架分析(五)-Widget,Element,RenderObject樹
Flutter框架分析(六)-Constraint
Flutter框架分析(七)-relayoutBoundary
Flutter框架分析- Parent Data
Flutter框架分析 -InheritedWidget