Flutter渲染原理

在这里插入图片描述
上图的渲染流程:GPU的Vsync信号同步到UI线程,UI线程使用Dart来构建抽象的视图结构,绘制好的抽象视图数据结构在GPU线程中进行图层合成,然后提供给Skia引擎渲染GPU数据,最后通过OpenGL或者Vulkan提供给GPU。可以发现,渲染的核心在UI Thread中,在UI Thread中Flutter框架会做如下操作:

  • 动画(Animate)阶段:因为动画会随每个Vsync信号的到来而改变状态(State),所以动画阶段是流水线的第一个阶段;
  • 构建(Build)阶段:需要被构建的Widget会在此阶段被构建,对应的函数StatelessWidget.build()或者State.build()被调用的时候;
  • 布局(Layout)阶段:会确定各个元素的位置、尺寸,对应的RenderObject.performLayout()被调用的时候;
  • 绘制(Paint)阶段:RenderObject.paint()被调用的时候;

Flutter如何实现高效渲染的

通过Flutter提供的Widgets,我们可以构造出各种界面,那这些Widgets是如何工作的,这就涉及到三棵树渲染机制了。
在Flutter中和Widgets相关的,还有Elements和RenderObjects,由于它们都具有树形结构,所以被称作三棵树。

  • Widget:Widget是Flutter的核心部分,是用户界面的不可变部分;
  • Element:Element是实例化的Widget对象,通过Widget的createElement()方法,是在特定位置使用Widget配置数据生成;
  • RenderObject:用于应用界面的布局和绘制,保存了元素的大小、布局等信息;

runApp(const HuApp());被调用时,会发生如下几个阶段:

  • Flutter会构建包含页面HuApp和其子节点的Widgets树;
  • Flutter遍历Widget树,然后根据其中的Widget调用createElement()来创建相应的Element对象,最后将这些对象组建成Element树;
  • 接下来会创建第三个树,这个树包含了与Widget对应的Element通过createRenderObject()创建的RenderObject;
    Flutter经过这三个步骤后的状态:
    在这里插入图片描述
    可以看出Element是Widget树和RenderObject树之间的桥梁,每一个Element中都有着对应的Widget和RenderObject的引用。
    当一个页面在刷新时,有些是改变的,有些是不变的。那么三棵树,就是为了避免频繁的创建和销毁不变的部分,以此提高渲染性能。实例化一个RenderObject的成本是很高的,频繁的实例化和销毁RenderObject对性能的影响很大,所以当Widget数改变的时候,Flutter使用Element树来对比新老Widget

('vm:prefer-inline')
Element? updateChild(Element? child, Widget? newWidget, Object? newSlot) {
    
    
  if (newWidget == null) {
    
    
    if (child != null) {
    
    
      deactivateChild(child);
    }
    return null;
  }
  final Element newChild;
  if (child != null) {
    
    
    bool hasSameSuperclass = true;
    assert(() {
    
    
      final int oldElementClass = Element._debugConcreteSubtype(child);
      final int newWidgetClass = Widget._debugConcreteSubtype(newWidget);
      hasSameSuperclass = oldElementClass == newWidgetClass;
      return true;
    }());
    ...

  return newChild;
}
  • 如果某个位置的Widget和新Widget不一致,才需要重新创建Element;
  • 如果某个位置的Widget和新Widget一致,则只需要修改RenderObject的配置,不用进行耗费性能的RenderObject重新构建;

在实际开发中,我们接触不到Element,实际上每个Widget的build(BuildContext context)方法中传递的context就是实现了BuildContext接口的Element。

其实在Flutter的这种设定中,Widget类似于配置文件,而最终是通过Element、RenderObject去做的渲染操作。所以Widget并不是真正的干活的,Widget的嵌套事实上并不是View的嵌套,不会造成性能问题。

猜你喜欢

转载自blog.csdn.net/Memory_of_the_wind/article/details/131372587