Flutter 基础架构、渲染流水线和状态管理

为什么需要 Flutter

Flutter 通过自建绘制引擎,具备与 Native 相媲美的性能,同时能够磨平平台开发的差异性。


Flutter 架构

Flutter 架构可分为 Dart APP 层、Flutter 框架层、引擎层(面向操作系统的 C++ 引擎以及面前 Web 的 Web 引擎)、平台嵌入层。

Flutter 框架层

Flutter 框架层实现一套基础库用于处理动画、绘图和手势,在这之上 Rendering 层用于组件的底层渲染的结构数据,Widgets 层面向开发者提供一套描述视图结构的语言,并基于绘图封装了两种风格的 UI 组件库

底层引擎层

引擎层可分为 C++ 引擎和 Web 引擎,其中 C++ 引擎包括 Skia 渲染引擎、Dart 运行时以及文字排版引擎等

设计思想

Widget 是视图的基本单元和配置信息

视图以 Widget 为单位,Widget 的状态描述了页面的状态,状态发生变化,Widget 会重新构建 Widget。

Widget 有以下特点:

  • widget 是不可变的

    Flutter 中 widget 重建是频繁发生,因此经常需要进行新旧 widget 树的比较。为了降低比较时间采用浅比较,widget 的 Immutable 特性能够很好契合这一使用场景。只要 widget 发生变化,就会返回一个新的 widget,从旧的 widget 创建一个新的 widget 采用了优化算法,达到快速比较和快速创建的性能

最小颗粒度更新渲染树

Flutter 会比对前后状态的变化,以确定底层渲染树从一个状态切换到下一个状态所需要的最小改变。

渲染流水线

Flutter 渲染视图的本质就是在 Vsync 信号中,通过 UI 线程快速的构建并生成抽象的视图结构数据,并将抽象视图结构数据发送给 GPU 线程,GPU 线程将视图结构数据进行光栅化和上屏;

首先从宏观上理解 Flutter 的渲染流水线:

  • Flutter 引擎启动时,通过平台线程,向系统注册接收 Vsync 信号的回调
  • UI 线程接收到 Vsync 信号后,会构建三棵树(widget/element/RenderObject)并生成布局信息,这一工作主要在 Flutter 框架层处理
  • 根据布局信息进行绘制,生成一系列的绘制指令组成 layer tree,这一工作主要在 Flutter 框架层处理
  • GPU 线程从 UI 线程中获取 layer tree,进行光栅化和上屏操作,这一工作主要在底层引擎层处理
  • 然后请求接收系统下一次的 Vsync 信号,又回到第 2 步

线程模型

理解 Flutter 的渲染流水线,需要先理解 Flutter 的线程模型,Flutter 的四大线程配合来完成渲染 UI 工作:

值得注意的是,Flutter Engine自己不创建管理线程。Flutter Engine线程的创建和管理是由embedder负责的。Embeder指的是将引擎移植到平台的中间层代码。

  • Platform 线程: Flutter 线程模型中的主线程,负责提供 Native 窗口,作为 GPU 渲染的目标。接受平台的 VSync 信号并发送到 UI 线程,驱动渲染管线运行。
  • UI 线程: 负责 UI 组件管理,维护 3 颗树,Dart VM 管理,UI 渲染指令生成。同时负责把承载渲染指令的 LayerTree 提交给 GPU 线程去光栅化。
  • GPU 线程: 通过 flow 模块完成光栅化,并调用底层渲染 API(opengl/vulkan/meta),合成并输出到屏幕。
  • IO 线程: 包括若干 worker 线程会去请求图片资源并完成图片解码,之后在 IO 线程中生成纹理并上传 GPU ,由于通过和 GPU 线程共享 EGL Context,在 GPU 线程中可以直接使用 IO 线程上传的纹理,通过并行化,提高渲染的性能

UI 线程

当用于描述 UI 的 widget 树发生变化,会触发 build 阶段,build 阶段后会进入 layout 阶段生成 RenderObject tree,layout 完后进入 paint 阶段生成绘制指令 layer tree。

build 阶段

Flutter 对视图结构的抽象描述采用了三棵树:widget tree、element tree 和 render tree。可把它们类比成:

widget tree => jsx
element tree => virtual dom
render tree => rendering tree (dom + cssom)

widget tree

widget tree 是 Dart 代码中所写的组件结构,描述 UI 元素的配置信息,widget 是 immutable 的,如果 widget 的状态发生更新,则会进行重建。实际业务场景中, widget 会频繁进行重建;

element tree

element tree 是 widget 的一种抽象,是视图中真正显示的元素,也是连接 widget tree 和 render tree 的桥梁;

当 widget 挂载到 widget tree 的时候,会调用 widget.createElement() 方法,创建其对应的 Element。Flutter 再将这个 Element 挂载到 element tree 并持有创建它的 widget 的引用

当 widget 发生变化时,会将其 element 标记为 dirty element,在下一次更新视图时根据这个状态只更新被修改的内容,从而提升渲染性能;

element 的更新时机是根据 widget 的两个属性进行的:

  • widget 的类型,前后组件是否为同一个类所创建
  • widget key

如果二者都没发生改变,则会复用该 element,并将新的 widget 配置数据更新到 element;

render tree

render tree 是真正进行组件布局渲染的工作,render tree 每个节点都是继承自 RenderObject 类对象,其由 Element 中的 renderObject 或 RenderObjectWidget 的 createRenderObject 方法生成;

布局阶段

UI 线程根据 RenderObject tree 进行布局,具体可分为两个线性过程:从顶部往下传递布局约束,从底部往上传递布局信息:

绘制阶段

UI 线程根据 RenderObject tree 生成绘制指令并形成 layer tree

状态管理

StatefulWidget

Flutter 的 widget 可分为 StatefulWidget 和 StatelessWidget。

  • stateless widget

    stateless widget 没有自己的内部状态,只用于展示从外部传递的或是内部的静态数据

  • stateful widget

    stateful widget 拥有自己的状态用于对外部做出反馈,widget 的状态保存在一个 State 对象中,并通过 setState 去重构 widget

StatefulWidget 的生命周期

  • 当 stateful widget 挂载到 widget 树时,会调用 widget.createState() 方法
  • 新创建的 State 会和 BuildContext(widget 对应的 element) 关联,此时 initState 会调用
  • 当 statefulWidget 使用 InheritedWidget 的数据时,并且 InheritedWidget 的 updateShouldNotify() 返回 true 时,说明 statefulWidget 的依赖数据发生变化,因此会触发 didChangeDependencies()
  • 进入 build 阶段构建视图
  • 当 State 暂时从视图树中移除或是页面切换时,该函数就会调用
  • 当 State 被永久从视图树中移除,该函数会调用
  • 当 widget 的配置发生变化时就会调用这个函数,例如 hot-reload
  • setState 会重新触发 build

理解 setState 的更新机制

setState()过程主要工作是记录所有的脏元素,添加到BuildOwner对象的_dirtyElements成员变量,然后调用scheduleFrame来注册Vsync回调。 当下一次vsync信号的到来时会执行handleBeginFrame()和handleDrawFrame()来更新UI。

状态管理机制

  • widget 自己管理自己的状态

    状态是 UI 状态,交由 widget 自身管理

  • 父 widget 管理子 widget 状态

    状态是用户数据,交由父 widget 管理

  • 混合管理

widget 间的通信机制

  • 父 widget 向子 widget 通信

    通过实例化子 widget 并向子 widget 传值到子 widget 的实例属性

  • 子 widget 向父 widget 通信

    通过设置回调函数形式

  • 兄弟组件间通信

    • 通过将数据挂载到同一个父组件
    • 数据共享的数量较少,使用 InheritedWidget(React Context)
  • 跨组件通信

    • 从上往下

      InheritedWidget

      class ShareDataWidget extends InheritedWidget {
        ShareDataWidget({
          @required this.data,
          Widget child
        }) :super(child: child);
      
        final int data; //需要在子树中共享的数据,保存点击次数
      
        //定义一个便捷方法,方便子树中的widget获取共享数据  
        static ShareDataWidget of(BuildContext context) {
          return context.dependOnInheritedWidgetOfExactType();
        }
      
        //该回调决定当data发生变化时,是否通知子树中依赖data的Widget  
        @override
        bool updateShouldNotify(ShareDataWidget old) {
          //如果返回true,则子树中依赖(build函数中有调用)本widget
          //的子widget的`state.didChangeDependencies`会被调用
          return old.data != data;
        }
      }
      
      class _TestWidget extends StatefulWidget {
        @override
        __TestWidgetState createState() => new __TestWidgetState();
      }
      
      class __TestWidgetState extends State<_TestWidget> {
        @override
        Widget build(BuildContext context) {
          //使用InheritedWidget中的共享数据
          return Text(ShareDataWidget
              .of(context)
              .data
              .toString());
        }
      
        @override
        void didChangeDependencies() {
          super.didChangeDependencies();
          //父或祖先widget中的InheritedWidget改变(updateShouldNotify返回true)时会被调用。
          //如果build中没有依赖InheritedWidget,则此回调不会被调用。
          print("Dependencies change");
        }
      }
      
      class InheritedWidgetTestRoute extends StatefulWidget {
        @override
        _InheritedWidgetTestRouteState createState() => new _InheritedWidgetTestRouteState();
      }
      
      class _InheritedWidgetTestRouteState extends State {
        int count = 0;
      
        @override
        Widget build(BuildContext context) {
          return  Center(
            child: ShareDataWidget( //使用ShareDataWidget
              data: count,
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Padding(
                    padding: const EdgeInsets.only(bottom: 20.0),
                    child: _TestWidget(),//子widget中依赖ShareDataWidget
                  ),
                  RaisedButton(
                    child: Text("Increment"),
                    //每点击一次,将count自增,然后重新build,ShareDataWidget的data将被更新  
                    onPressed: () => setState(() => ++count),
                  )
                ],
              ),
            ),
          );
        }
      }
      
      
    • 从下往上

      Notification 通知冒泡

      class MyNotification extends Notification {
          final String msg;
          Mynotification(this.msg);
      }
      
      class NotificationRoute extends StatefulWidget {
          @override
          NotificationRouteState createState() {
              return new NotificationRouteState();
          }
      }
      
      class NotificationRouteState extends State {
          String _msg = "";
          @override
          Widget build(BuildContext context) {
              return NotificationListener(
                  onNotification: (notification) {
                      setState(() => { _msg += notification.msg + " "; })
                      return true;
                  },
                  child: RaisedButton( 
                      onPressed: () => MyNotification('hi'),dispatch(context),
                      child: Text('Send Notification'),
                  ),
              );
          }
      }
      
      
  • Event Bus

    适用于共享数据量较少的跨组件通信场景

  • 状态管理工具

    • flutter_redux
    • flutter_mobx
    • bloc

参考资料

Flutter原理:三棵重要的树(渲染过程、布局约束、应用视图的构建等)

硬件绘图原理

Flutter 实战

Flutter 跨平台演进及架构开篇

Flutter UI 绘制原理引导

Flutter WebView 与 JS 通信

Flutter WebView与JS交互简易指南

Dart

你可能感兴趣的:(Flutter 基础架构、渲染流水线和状态管理)