最右 JS2Flutter 框架——通信机制(三)
1、概述
通信包括异步和同步两种方式,异步可根据是否关注返回结果再细分成Request-Reply和One-Way两种。JS2Flutter框架的通信机制也是在不断的迭代中逐步完善,年初发的文章Flutter动态化在最右App中的实践[1]中还是通过对JSWorkThread的阻塞,去实现Client侧到Host侧的单向同步,如今已经完成了Client和Host之间真正意义上的双向同步直连。
2、异步
2.1 One-Way
对于单向无需关注返回结果的调用,实现起来很简单,在最右JS2Flutter框架——开篇[2]中,我们讲述了Hello World实现的例子,这就是一个最简单的One-Way调用,Client和Native之间可以通过建立通信,Native和Host之间可以通过PlatformChannel建立通信,这样就完成Client和Host之间的通信。
2.2 Request-Reply
对于One-Way的调用,很容易就可以实现,假如我们需要一个返回结果呢?
举个例子,开发者想要获取剪切板数据,调用了Clipboard.getData,在运行阶段这个函数是在JS运行的,它必须向Host侧真实的Clipboard.getData发起调用,Host侧在拿到结果之后,通知Client侧,Client侧把结果给调用者。
很容易想到Client侧通过JSCore向Native注册回调函数,Native通过MethodChannel设置MethodCallHandler,当Native收到Host侧的回调时,Native调用Client侧的回调函数。JS2Flutter框架就是这样去实现的,但却又不止于此,仅仅使用回调函数来做异步的话,当遇到大量的异步回调场景时,很可能陷入"回调地狱",其实Dart为了避免类似的问题,引入了Future,JS也引入了Promise。回调函数只作为Native回调Client侧的一个句柄,真正在框架中发挥威力的是通过Future和Completer去实现的callFlutterWithReply。
至此,Client侧面向Host侧的异步调用便建立起来了,同理,可建立Host侧面向Client侧的异步调用,这样就完成了双向的异步通信机制。
3、同步
同步的需求场景也很多,我们在Flutter动态化在最右App中的实践[1]中也有列举,比如你要通过TextPainter来测量文字的高度,是需要同步机制才能保证正确性的,当时我们是通过阻塞JSWorkThread去实现Client侧的同步,因为这是一个与UI绘制无关的线程,所以它的阻塞并不影响渲染和事件接收,但后来我们发现同步机制不仅是Client侧需要的,Host侧也有可能需要。
举个例子,我们在实现ListView控件时,如果是通过builder方式去构建,当遇到大量数据的时候,因为每一项都是实时构建,需要先询问Client侧该位置的子树并立即还原,如果是异步去刷新的话,很可能后几项先显示出来,从而造成视觉上的错乱。所以,在Host侧我们也需要建立同步机制。
那在Host侧是否可以采用同样的方案建立同步机制呢?当然也是可行的,但最右没有采用这种方案,试想一下,本来JS到Flutter就需要经历JSWorkThread、MainThread、Flutter UIThread,再来一层循环阻塞,效率肯定是大打折扣。我们采用了更高效的方式,让JS运行在Flutter的UI线程里,构建一套Client和Host之间直连的通信渠道,这样就能实现真正意义上的双向同步。
这个过程中我们需要解决两个问题:
1:如何让代码块儿能在Flutter UI线程执行?
2:怎样才能建立直连通信渠道?
要解决第一个问题,我们需要了解Flutter UI线程Work的方式,这里直接借用Gityuan的博客——Flutter渲染机制——UI线程[3],Engine是通过UITaskRunner提交任务,从而让任务在UI线程执行。理解了这一点,我们就可以扩充FlutterEngine的能力,增加一个postTaskOnUIThread函数,XCJSRuntime在启动时,让JS运行在Flutter UI线程。
第二个问题相对要复杂的多,牵涉到的面也比较广,我们先看看系统是如何建立通信渠道的,不了解的同学可以看看Gityuan的博客——深入理解Flutter的Platform Channel机制[4],理解了系统的流程(博客以Android为例,iOS侧同理)之后,首先要思考的就是我们与系统不一样的地方,Platform Channel是为了在Flutter和Platform之间建立通信渠道,它们在不同的线程,而我们是在同一个线程,所以我们不需要再去转线程。举个例子:
有了这个基础之后,我们就可以在MethodChannel上拓展一个invokeDirectMethod函数,参照系统的实现方式,修改Dart层和Engine层,修改的路线也基本就是MethodChannel的调用流程。
这里我摘取了_DefaultBinaryMessenger中增加直连的修改,其后续的实现步骤基本上都是跟随着PlatformChannel的流程,增加直连的函数。
4、结束语
本⽂阐述了最右JS2Flutter框架的通信机制,基本上都是参照了Flutter的实现⽅式,对于同步直连的实现过程,涉及到的环节比较多,也只阐述了思想,对细节实现感兴趣的同学可追溯源码一探究竟。
5、参考文献
[1]:Flutter动态化在最右App中的实践 https://www.infoq.cn/article/MIa5AN2JE51uor4JeiPG
[2]:JS2Flutter框架——开篇 https://xie.infoq.cn/article/acee65b914dc4d0e32a5561a1
[3]:Flutter渲染机制——UI线程 http://gityuan.com/2019/06/15/flutter_ui_draw/
[4]:深入理解Flutter的Platform Channel机制 http://gityuan.com/2019/08/10/flutter_channel/
版权声明: 本文为 InfoQ 作者【刘剑】的原创文章。
原文链接:【http://xie.infoq.cn/article/f23e562e3aa7f3c198eb40a83】。文章转载请联系作者。
评论