flutter_hybird_webview 跨进程渲染的实践技术分享


theme: channing-cyan

前言

大家好,继 Flutter跨进程混合栈渲染的实践——子进程WebView 之后,利用业余时间对整个插件进行了完善和基础测试,诚然距离投入生产还有一段距离,但总算能达到beta阶段了(仓库地址在文尾)。

这个插件断断续续开发了有小半年,最初只是为了隔离webview,但随着不断地实践,发现在app容器化小程序等方面也可能有不错的应用和表现。故,在此做一个记录和分享,希望能帮到有需要的朋友,同时对于不足的地方,也望大佬能给予指点。

限于篇幅,此文仅对插件做一个整体性的介绍,后续会对具体的子模块做详细介绍。

介绍

目前仅支持android端,以下也基于Android平台进行介绍

flutter_hybird_webview旨在将webview隔离到子进程以避免其对主进程的影响,同时为了不增加栈的管理复杂度,采取子进程渲染,主进程显示的方式来保持flutter栈的唯一性。

演示(多图)

gif已转为webp。总计约10m+

flutter_hybird_webview 跨进程渲染的实践技术分享

flutter_hybird_webview 跨进程渲染的实践技术分享

flutter_hybird_webview 跨进程渲染的实践技术分享

flutter_hybird_webview 跨进程渲染的实践技术分享

技术原理

组织架构图

老版架构图,结构一致,细节略有变动。

flutter_hybird_webview 跨进程渲染的实践技术分享

原理介绍

下面对插件的主要模块进行介绍

渲染模块

flutter端采用的是texture widget,其特点是可以配合平台端的surface进行显示。

当我们在flutter侧通过widget构建一个页面时,最终会在engine侧形成一个layer treeskia会通过里面录制的绘制指令,绘制(skia底层绘制方式还是由平台来决定的,如opengl,vulkan,metal等)到surface所指向的内存缓冲区。

flutter_hybird_webview 跨进程渲染的实践技术分享

rasterizer runner为engine在app进程开启的一条线程,主要负责绘制相关。
还有诸如:platform、io、ui等Runner.

如果了解绘制原理,应该知道surface是由平台侧提供的,而平台侧的surface对应Surface Flinger服务进程的layer(一一对应关系),其内部含有一个BufferQueue可以根据需要来申请一块内存缓冲区并通过映射共享给其他进程(如app)。简单讲,surface内部包含一个指针并指向了一块内存区域,我们绘制的数据最终会在那个内存区域里。

这里不做过多介绍,有兴趣的可以百度

由上面我们可以知道surface是具有跨进程能力的,那么我们便可以通过在主进程创建surface然后共享到子进程(甚至可以反过来),并由其来提供绘制指令。

flutter_hybird_webview 跨进程渲染的实践技术分享

通过上述方式便确保了栈的唯一性,降低了管理栈所带来的额外成本。

Touch event模块

    Touch event由硬件传感器生成并最终传递到app,Input event则由输入进程(包括那个软键盘窗口)
    产生并传递到app,也就是说这些event 天然具备跨进程传递。

Flutter的触摸事件是由平台侧传递过来,并进行分发的,可参考Flutter——原生View的Touch事件分发流程 以及更早的文章。

起初我是打算在平台侧做拦截并进行分发的,但考虑到view在具体使用时的环境可能非常复杂,如 部分显示表面有浮层可见但无法消费事件等等,所以放弃了这个方案,并参考google的实现,改由在flutter侧处理并回传到平台层,再分发到子进程。

flutter_hybird_webview 跨进程渲染的实践技术分享

这里之所以增加event coordinator 模块,是因为view在具体显示的时候并非充满整个页面,需要将点击坐标转换成相对坐标分发到子进程的remote view

Input event模块

由于软键盘的拉起,需要view获得focus以及display id的匹配,官方的trick并不能解决在多进程下无法拉起软键盘的问题。

经过思考得出两个方案:

方案一

通过对源码的分析,可以知道app是通过binderIMS通信来实现输入功能的,那么我们对IMS的binder : IInputMethodManager进行代理以监听软键盘的show请求,然后通过app的binder通知主进程代劳,并将input event分发到子进程,便可以完成这个功能。

一通理论分析后,便信心满满的开干,结果被现实打得鼻青脸肿…Android较新的版本,不仅binder有缓存,就连imm也增加了缓存,而且由于高版本的api限制导致无法正常hook。在通过进一步研究和大佬的指点,发现可以通过native层的hook或者匿名调用来绕过限制。

由于工程量较大,所以插件的实现暂未采用方案一,而是临时采取方案二

方案二

在仔细分析了android和chromium的input模块的通信流程和源码后,采用了通过对getSystemService方法重写,然后对调用堆栈进行分析,来判断webview是否请求了软键盘的弹出操作。

具体运行效果尚可,但个人的目标还是方案一,后续将会抓紧时间来实现此方案。

通信模块

整个插件按照通信端来划分,可以分为3部分:

flutter端
平台端
子进程端

flutter_hybird_webview 跨进程渲染的实践技术分享

插件的管理及widget/view的内部通信都是基于这个通信架构来设计开发的,通信实现由channelbinder配合完成。为了降低通信管道的复杂度,对通道进行了属性上的划分:管理通道私有通道

管理通道: 负责整个插件的生命周期管理及维护命令的分发。
私有通道:view内部的私有通道,处理定制化命令的分发。

具体通信流程如下图:

flutter_hybird_webview 跨进程渲染的实践技术分享

后语

至此整个插件的基本介绍就到此结束了,由于一些子模块涉及的东西较多,所以会在后续文章中做详细介绍,谢谢大家阅读,如有错误还望指出。

仓库地址

暂时不能用于生产哦~ 具体分支含义见仓库的readme。

flutter_hybird_webview

其他文章

Flutter——原生View的Touch事件分发流程

Flutter在Android平台上启动时,Native层做了什么?

Flutter版 仿.知乎列表的视差效果

Flutter——实现网易云音乐的渐进式卡片切换

Flutter 仿同花顺自选股列表

上一篇:poj 3253 Fence Repair(优先队列+哈夫曼树)


下一篇:asp.net form身份认证不定时认证失败的问题 排查