Skip to content

Latest commit

 

History

History
57 lines (30 loc) · 6 KB

基于Native的三端通信机制.md

File metadata and controls

57 lines (30 loc) · 6 KB

基于Native的三端通信机制

在 Flutter 动态化中,通信是基石,所有的一切都是建立在通信上的,需要通信的双方分别是:JS & Flutter.

JS 与 Flutter 是依赖于 Native 又完全独立的两端:JS 中的数据运算与流转不会直接影响到 Flutter 页面的渲染;Flutter 的渲染过程也不会阻塞 JS 的代码执行。

为了让完全独立的两者产生联系,我们找到了一个既能与 JS 产生联系,又能与 Flutter 传递消息的媒介 —— Native. 通过将一个消息从一端传递给 Native,再由 Native 完整传递给另一端,就实现了 JS 与 Flutter 之间的通信。

搭建三端通信链路

在 Flutter 初始化时,Flutter 会与 Native 通过 methodChannel 建立通信关系,methodChannel 是一条双向通信的链路,既可以在 Flutter 中接收到 Native 的消息,也可以主动向 Native 发出消息。

同时,Native 在执行 JS 代码之前会向 JS 的 context 中注入一个方法,我们将这个方法命名为 methodChannel_js_call_flutter,用来使 JS 能够向 Flutter 传递消息。

methodChannel_js_call_flutter 方法的实现方是 Native,但是调用方(消息的传递方)是 JS,最终消息的接收方式 Flutter,因此在命名中忽略了 Native,只保留了消息发出和接收的双方,以便代码的直观和便于理解。同时还有另一个方法 methodChannel_js_call_native,该方法的消息接收方是 Native,通过这种方式命名可以有效区分这两个方法。

在 Flutter 动态化中,一个完整的跨三端的通信链路一般是:

三端通信链路图

从上面两个链路中会发现,第一条链路是完整的,消息可以顺利到达 Flutter;但是第二个链路在 Native 中断掉了,没有一个通道能够将消息传递到 JS 中。为了解决这个问题,JS 会在 context 中暴露一个名为 methodChannel_flutter_call_js 的方法,该方法的参数即为消息内容,这样 Native 就能够直接调用该方法将消息传递到 JS.

“半双工”通信与通信反馈

在动态 Flutter 中,几乎所有的三端通信需求都是“半双工”的。此处的“半双工”指的是,当一方作为消息传递方时,无法通过当前传递消息的通道获得消息接受方的反馈。这就表示当传递方发送出一条消息后就会结束自己的通信行为,它们不需要去关心自己是否会得到反馈,而实际上也不会有任何反馈。

基于以上情况,动态 Flutter 中的所有通信链路都会使用这种模式进行通信:消息传递方只需要传递数据而不需要关心回调,消息接收方只需要处理数据而不需要返回处理结果。这种模式对于跨越三端的通信来说更便于管理和约束,也使得 Native 成为了一个完全的数据中转站,否则 Native 除了需要传送数据外,还需要处理结果的反馈工作。于是,一条清晰的数据流转路线便形成了:数据传递方 -> 数据中转方 -> 数据接收方

但是并不是所有的通信都不需要反馈,例如与 Native 通信的双端通信链路 bridge,在向 Native 发出通信消息后需要获得 Native 的处理结果。对于这种情况,简单粗暴的单向通信将无法直接满足需求。但如果换成携带回调的“全双工”通信,从而能够在同一个通信通道上实现结果的接收,将会破坏原有的通信模式,也为通信的管理增加了难度。

为了解决在“半双工”通信模式上的通信反馈问题,我们通过在传递方为每一个需要反馈的通信加上标识符,再将反馈处理方法通过标识符缓存;当接收方处理完成后,携带标识符通过另一个通信通道将处理结果作为一个新的消息传递给原本的传递方后(在这个新的通道中,原本的数据传递和接收方将会互换身份),传递方会根据标识符在缓存中查找到处理方法并执行处理逻辑。

确保首次通信的正确接收

JS 与 Flutter 的通信是 Flutter 动态化的基石,而首次通信的成功与否又是通信能否成功建立的首要条件。

由于所有的跨三端通信都是“半双工”的,而 JS 与 Flutter 的环境准备又各自完全独立,这也就导致如果任一方环境准备完成前,另一方就发送了消息,这就会出现环境未完成的一方无法接收到消息的情况,从而影响后面所有的通信,导致通信中断或错乱。

为了解决这种情况,JS 与 Flutter 中采取了以下策略来保证首次通信的顺利执行(以下以 A / B 代指 JS 与 Flutter 中的任一方):

  1. A 环境准备完成后会立即向 B 发送通知;
  2. 如果 B 已准备好则会立即回复一条通知,A 收到回复通知后标记双方环境已建立,可进行后续的通信;
  3. 如果 B 未准备好,则 A 将不会收到任何回复,直到 B 准备好,此时A / B 身份互换,会重新回到步骤 1.

结束语

通过建立通信链路,并且约定好消息格式与处理方法,可以轻易得将一端的数据发送到另一端正确的处理逻辑中,从而实现建立页面、更新组件、事件触发等等一系列的操作。

当然,跨越三端的通信链路已然偏长,环节越多耗时会越长、出现异常的可能性也会越多。如果某一次通信耗时过长则会给用户带来明显的迟滞感;发生错误也会导致用户操作或页面无响应——这些也正是动态 Flutter 不断优化与成长的方向之一!