微信交流群

作者:易旭昕

原文链接:https://zhuanlan.zhihu.com/p/158145375

写作费时,敬请点赞,关注,收藏三连。

# Flutter 当前的问题

混合应用中 Per FlutterView Overhead 过高的问题,对于每个 FlutterView,每个 Engine 都需要分配:

  1. 一个 Root Isolate;
  2. 3 个线程(UI,GPU,IO);
  3. 两个 GL Context(Android);
  4. 一个光栅化的 Skia GrContext,一个纹理上传的 GrContext(后者可以忽略不计);

每增加一个 FlutterView,都需要付出较多的额外系统资源和内存占用。系统资源包括线程和 GL Context,另外它们本身也需要额外的内存分配,创建线程和 GL Context 也存在额外的时间开销。多个光栅化的 Skia GrContext,也妨碍了 Skia 内部共享缓存,导致额外的内存占用和额外的 WarmUp 开销。

并且 Flutter 的图片解码和缓存管理机制,也存在在复杂场景下缺少有效的峰值控制机制等问题,在大量图片同时生成的时候容易出现系统阻塞和 OOM。

除此之外,对于混合应用来说,Flutter 还缺失了在不同 Flutter App 之间共享数据的通用机制(Flutter App host 在不同的 Flutter View)。

关于 Flutter 的图片解码和缓存管理机制,更多的信息请参考文档 Flutter 图片解码与缓存管理研究

# 我们的目标

我们的目标就是希望 FlutterView 和 WebView 一样(甚至表现更好):

  1. FlutterView 可以灵活地在各种不同场景下使用,全屏,内嵌作为 Tab,Card,Cell 等等;
  2. 创建 2+ FlutterView 对系统资源,和内存占用的额外占用较小,初始化开销更小,应用可以更放心地同时使用多个 FlutterView;
  3. 在复杂业务场景下提供有效的内存峰值控制机制,减少业务对内存占用的担心,并且不需要业务的 Flutter App 修改代码;
  4. 在 Framework 和容器层提供一套 API ,实现数据共享(Flutter App 之间,Flutter 和 Native 之间);
  5. 在容器层面实现混合栈管理和页面导航;

# 第三方方案调研

# Multi Window

这个 PR 是为桌面应用提供多窗口的支持。从代码和文档来看,主要使用场景看起来应该是在 Framework 层提供一套窗口创建和管理的 API,供 Flutter App 调用这些 API 创建额外的 Window,并且显示另外的 Widget 树。虽然跟我们的使用场景有一些交集,但实际上还是有比较大的差别。

并且目前提交的代码只包括了初期的代码重构,引入多窗口的接口支持。窗口创建和管理部分还是欠缺的,另外在一个 Isolate 里面同时支持多棵 Widget/Render 树也没有看到相关的解决方案。

总的来说这个方案目前没有太大参考价值。

# 头条的混合方案

头条的方案参考这篇文章让Flutter真正支持View级别的混合开发。因为头条实际上并没有开源,文章提供的信息也比较有限,缺少足够的细节作为参考。比如文章里面仅仅提到了在一个 Isolate 里面支持多窗口的方案,而多棵 Widget/Render 树如何共存并没有看到相关的解决方案。另外线程配置,GL Context 等也没有提及,如果还是多组线程,多个 GL Context,能够减少的资源占用也很有限。另外就是这套方案需要对 Framework 层做较多改动,可能存在的问题包括破坏应用 Flutter App 的兼容性,难以升级,难以保证修改的完整性等等。

所以这个方案实际上缺少足够信息作为参考,并且方向上存在较大的不确定性。

# 官方的改进方案

官方的这份文档Multiple Flutters给出一个改进方案,明确了以下设计:

  1. 每个 FlutterView 拥有独立的 Root Isolate 运行自己的 Flutter App,通过一个 Shared Isolate 共享数据;
  2. 每个 FlutterView 的 Engine 有自己独立的 UI 线程,GPU 线程和 IO 线程是共享的,这也意味着 GL Context/GrContext 是共享的;

那么 2+ FlutterView 的额外开销就是一个线程和一个 Isolate 的开销,官方给出的数据是 2M 左右,应该说还算不错。

这个方案相对来说最大的优点是确定性比较高,改动基本不涉及 Framework 层面,集中在 Engine 层面,对应用来说基本无感。

比较不确定的主要是目前 Dart VM 是否支持 Shared Isolate,并且 Root Isolate 运行在不同的线程,那么它们对 Shared Isolate 的访问时异步的方式还是同步加锁?另外对我们来说,如果需要在 Native 和 Flutter 之间共享数据,数据放在 Native 端可能会更合适。

不过这个方案仅仅在设想阶段,还没有相关的设计和实现可以参考。