乐趣区

Channel-flutter平台层与运行层的双向通信

环境: 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) 就先不分析了,本质与原理还是一样的。

响应发送回复

可以看到 DartMessengerpendingReplies:Map<>缓存了 BinaryMessenger.BinaryReply, 待 dart 代码执行完发送端操作后响应handlePlatformMessageResponse 时取出, 完成发送反馈, 在 MethodChannel 中即为方法返回值.

数据接收示例

接收 dart 层通知
目前代码中只有 AccessibilityChannel 有用到 BasicMessageChannel.MessageHandler, 这是为了设置 android 视图ViewAccessibility属性, 平常开发不怎么用到, 但毫无疑问, 最终调用的还是平台层的相关代码

接收 dart 层调用 - 以 Android 端调用平台类 PlatformChannel 为例
PlatformChannel 负责 dart 层向平台层调用的统一操作, 其创建过程如下

FlutterView.FltterView()
  new PlatformChannel
    new MethodChannel
    MethodChannel.setMethodCallHandler
      BinaryMessenger.setMessageHandler -> DartExecutor.setMessageHandler
        DartMessenger.setMessageHandler
  new PlatformPlugin
    PlatformChannel.setPlatformMessageHandler

DartMessengermessageHandlers 根据通道名称缓存了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 机制中最为重要的核心类, 是在平台层负责与运行层通信的最关键角色.

退出移动版