環境: flutter sdk v1.5.4-hotfix.1@stable
對應 flutter engine: 52c7a1e849a170be4b2b2fe34142ca2c0a6fea1f
存在這樣的情形: flutter應用的檢視控制元件響應使用者的輸入(比如KeyEvent), 需要將平臺的按鍵資料傳遞到flutter的dart環境並響應, 同時應用可能因為某個操作需要呼叫平臺的介面讓手機震動. 但是flutter的App檢視執行dart程式碼,平臺(Android)執行Java程式碼, 同時dart層無法識別java層定義的物件類, 這就需要將資料在不同的執行環境中傳遞, flutter框架中的channel機制其實就是實現這個目的的.
一些文章和部分程式碼可能會讓人感到困擾, 為什麼已經有send介面了還要新增一個setMessageHander介面, 同時send已經有回撥reply了, 怎麼MessageHandler除了資料還有帶一個reply.
理解的關鍵其實就是這個channel, 顧名思義, 就是進行資料傳送的通道, 在平臺層(java)與執行層(dart)進行資料通訊. 一旦涉及通訊就涉及物件傳遞, 而在不同執行時(runtime)環境進行物件傳遞就必然涉及物件序列化了. 所以不用被名稱迷惑, 所謂的MessageCodec
其實就是專門作物件序列化的例項, 而通道既然能傳送資料也必須能夠接收資料, 如此的雙向通訊, 僅此而已.
一個通道關聯3個物件: 名稱, 操作與序列化, 操作即具體做收發訊息的工作, 即Messenger
. 而訊息按型別又分為普通物件, 操作方法, 資料流, 對應著3種基本通道: BasicMessageChannel<T>, MethodChannel, EventChannel
####傳送有時機, 接收無定時 平臺端(android)可以顯式的建立一個通道, 通道建立後既可作為傳送端又可作為接收端, 作為傳送端可以主動的傳送相關資料, 是為有時機, 作為接收端, 只能被動等待資料到來, 是為無定時
####資料傳送
呼叫一個通道的send方法,即為傳送資料了, 有時傳送完資料需要一個反饋, 於是有另一個回撥引數Reply<T>
, 這個回覆是接收端反饋給傳送端後傳送端作的響應, 可以叫做傳送回覆.
####資料接收
每種通道都設定了一個setMessageHandler
的方法, MessageHandler<T>
其實就是通道的資料接收器, 更容易理解的名字應該是MessageReceiver
, 專門等待傳送端傳送的資料; 表示通道建立後作為接收方接收資料後進行的處理, 資料處理完之後可能需要再反饋給傳送端, 所以MessageHandler<T>.onMessage(T message, Reply<T> reply)
中的Reply<T>
是接收端反饋給傳送端的回覆, 可以叫做接收回復
####通道解碼
理解了通道本質, 通道的解碼MessageCodec
就顯而易見了, 也就顯得不那麼重要了: 在資料通訊過程中針對各種各樣的資料物件進行序列化和反序列化. 我們自己也完全可以定製自己的序列方式(比如gson), 因為無論是c++層java層還是dart層, 只能讀寫位元組.
可以總結如下: 通道的本質即資料通訊 通道的解碼即對資料進行序列化和反序列化 通道可作為傳送端也可作為接收端 通道最終是以二進位制位元組的形態傳送資料 c++消彌平臺的差異(android,ios), 同時提供統一的介面和方式供dart使用
####資料傳送示例
普通物件傳遞-以Android端傳遞按鍵事件至dart端為例
按鍵資料被包裝成一個物件例項,通道物件型別是BinaryMessenger<Object>
呼叫序列如下:
FlutterView.onKeyDown
AndroidKeyProcessor.onKeyDown
KeyEventChannel.keyDown
BasicMessageChannel.send
BinaryMessenger.send -> DartExecutor.send
DartMessenger.send
FlutterJNI.dispatchPlatformMessage
複製程式碼
最終呼叫了BinaryMessenger
的send方法, 其實現體是DartExecutor
, DartExecutor
是平臺層與執行層互動的點, 它實現了平臺向dart呼叫, dart向平臺的響應.
呼叫dart方法-以Android端傳遞導航事件至dart端為例
activity響應開啟頁面的方法onNewIntent被flutter定義了一個導航方法,通道物件型別是MethodChannel
, 呼叫序列如下:
FlutterActivityDelegate.onNewIntent
FlutterActivityDelegate.loadIntent
FlutterView.setInitialRoute
NavigationChannel.setInitialRoute
MethodChannel.invokeMethod
new MethodCall
JSONMethodCodec.encodeMethodCall
BinaryMessenger.send -> DartExecutor.send
DartMessenger.send
FlutterJNI.dispatchPlatformMessage
複製程式碼
可以看到方法的名稱與引數被包裝成了MethodCall
, 結構體被序列化成了位元組之後傳遞給dart, 最終還是呼叫了DartMessenger
的send方法
此外還有EventChannel
,但是在程式碼中沒有例項化(2019.06.24 flutter-engine:52c7a1e8)就先不分析了,本質與原理還是一樣的。
####響應傳送回覆
可以看到DartMessenger
用pendingReplies:Map<>
快取了BinaryMessenger.BinaryReply
, 待dart程式碼執行完傳送端操作後響應handlePlatformMessageResponse
時取出, 完成傳送反饋, 在MethodChannel
中即為方法返回值.
####資料接收示例
接收dart層通知
目前程式碼中只有AccessibilityChannel
有用到BasicMessageChannel.MessageHandler
, 這是為了設定android檢視View
的Accessibility
屬性, 平常開發不怎麼用到, 但毫無疑問,最終呼叫的還是平臺層的相關程式碼
接收dart層呼叫-以Android端呼叫平臺類PlatformChannel為例
PlatformChannel
負責dart層向平臺層呼叫的統一操作, 其建立過程如下
FlutterView.FltterView()
new PlatformChannel
new MethodChannel
MethodChannel.setMethodCallHandler
BinaryMessenger.setMessageHandler -> DartExecutor.setMessageHandler
DartMessenger.setMessageHandler
new PlatformPlugin
PlatformChannel.setPlatformMessageHandler
複製程式碼
DartMessenger
用messageHandlers
根據通道名稱快取了BinaryMessenger.BinaryMessageHandler
, 平臺層作為接收方不定時等待dart層傳送資料, 方法呼叫流程如下:
DartMessenger.handleMessageFromDart
BinaryMessenger.BinaryMessageHandler.onMessage -> MethodChannel.IncomingMethodCallHandler.onMessage
MethodCallHandler.onMethodCall -> PlatformChannel.parsingMethodCallHandler.onMethodCall
PlatformMessageHandler.vibrateHapticFeedback -> PlatformPlugin.mPlatformMessageHandler.vibrateHapticFeedback
PlatformPlugin.vibrateHapticFeedback
View.performHapticFeedback
複製程式碼
由上可見DartMessenger
是channel機制中最為重要的核心類, 是在平臺層負責與執行層通訊的最關鍵角色.