Flutter的UI渲染流程源码讲解

本篇文章意在讲解Flutter测量、布局和渲染和android的区别,了解了布局的渲染自己在flutter的自定义控件中才能如鱼得水,首先先从整个渲染的入口Mian.dart的main方法的runApp方法

void runApp(Widget app) {
  WidgetsFlutterBinding.ensureInitialized()
    ..scheduleAttachRootWidget(app)
    ..scheduleWarmUpFrame();
}

’这个方法主要做了三件事情,第一 WidgetsFlutterBinding.ensureInitialized()创建了WidgetsFlutterBinding的单例类,不管是渲染还是手势识别都是由这个类负责调度分配,scheduleAttachRootWidget创建根widget并且从根widget向子孩子递归创建元素Element,对继承子RenderObjectWidget的小部件创建RenderObject,从而创建出View的渲染树,scheduleWarmUpFrame方法会执行真正的渲染。

先来看一看WidgetsFlutterBinding.ensureInitialized()方法

 static WidgetsBinding ensureInitialized() {
    if (WidgetsBinding.instance == null)
      WidgetsFlutterBinding();
    return WidgetsBinding.instance;
  }

app刚启动的时候第一次WidgetsBinding肯定没有创建,那么会创建WidgetsFlutterBinding类对象,也就是执行到下面的构造函数

BindingBase() {
    developer.Timeline.startSync('Framework initialization');

    assert(!_debugInitialized);
    initInstances();
    assert(_debugInitialized);

    assert(!_debugServiceExtensionsRegistered);
    initServiceExtensions();
    assert(_debugServiceExtensionsRegistered);

    developer.postEvent('Flutter.FrameworkInitialization', <String, String>{});

    developer.Timeline.finishSync();
  }

构造函数里执行了initInstances方法,这里要理解一下dart语言中的mixin关键字的用法,例如

mixin A on Base{
void go(){
print(“aaa”)

}
}

abstract class Base
{
abstract  void go();
}

mixin B on Base{
void go(){
print(“bbb”)
super().go();
}

}
class C extend Base  with A,B{
void go(){
print(“ccc”)
super.go();
}
}


void main(){
C().go();
}

那么main方法执行的时候会先执行C的go方法而super.go()方法会先执行B的go方法,再去调用A的go方法。

再回源码WidgetsBinding这个类,第一个被执行的initInstances方法会先执行WidgetsBinding 的initInstances方法,也就是说从最外层开始执行同样的方法

class WidgetsFlutterBinding extends BindingBase with GestureBinding, ServicesBinding, SchedulerBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {

  /// Returns an instance of the [WidgetsBinding], creating and
  /// initializing it if necessary. If one is created, it will be a
  /// [WidgetsFlutterBinding]. If one was previously initialized, then
  /// it will at least implement [WidgetsBinding].
  ///
  /// You only need to call this method if you need the binding to be
  /// initialized before calling [runApp].
  ///
  /// In the `flutter_test` framework, [testWidgets] initializes the
  /// binding instance to a [TestWidgetsFlutterBinding], not a
  /// [WidgetsFlutterBinding].
  static WidgetsBinding ensureInitialized() {
    if (WidgetsBinding.instance == null)
      WidgetsFlutterBinding();
    return WidgetsBinding.instance;
  }
}

和渲染相关的操作都是RendererBinding负责处理,那么来看看它的实现方法

void initInstances() {
    super.initInstances();
    _instance = this;
    _pipelineOwner = PipelineOwner(
      onNeedVisualUpdate: ensureVisualUpdate,
      onSemanticsOwnerCreated: _handleSemanticsOwnerCreated,
      onSemanticsOwnerDisposed: _handleSemanticsOwnerDisposed,
    );
    window
      ..onMetricsChanged = handleMetricsChanged
      ..onTextScaleFactorChanged = handleTextScaleFactorChanged
      ..onPlatformBrightnessChanged = handlePlatformBrightnessChanged
      ..onSemanticsEnabledChanged = _handleSemanticsEnabledChanged
      ..onSemanticsAction = _handleSemanticsAction;
//创建根RenderView
    initRenderView();
    _handleSemanticsEnabledChanged();
    assert(renderView != null);
    //自动添加回调函数到对象
    //在渲染的时候调用
    addPersistentFrameCallback(_handlePersistentFrameCallback);
    initMouseTracker();
  }

这个方法作用是创建 了PipelineOwner这个类(它主要是来负责view树的管理,执行view树的遍历操作布局渲染等操作),然后设置widow的一些回调方法,这些方法是和底层交互用的,暂且不讨论,initRenderView是用来创建根View的,addPersistentFrameCallback方法是添加真正渲染的时候的回调方法,也就是在_handlePersistentFrameCallback方法里面实现view树的测量布局和渲染的,渲染流程咱们只关注RendererBinding。

初始化工作做好之后,接下来是调用scheduleAttachRootWidget方法将所有view构建成树

 void attachRootWidget(Widget rootWidget) {
    //创建根view
    _renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
      container: renderView,
      debugShortDescription: '[root]',
      child: rootWidget,
      //renderViewElement就是RenderObjectToWidgetAdapter
    ).attachToRenderTree(buildOwner, renderViewElement);
  }

首先创建RenderObjectToWidgetAdapter继承自RenderObjectToWidget,创建完之后开始真正的渲染树的创建如下:

 RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement<T> element ]) {
    //首次进来等于null
    if (element == null) {
      owner.lockState(() {
        //创建根RenderObjectToWidgetElement
        element = createElement();
        assert(element != null);
        //关联构造所有者
        element.assignOwner(owner);
      });
      owner.buildScope(element, () {
        //执行根element的rebuild
        element.mount(null, null);
      });
      // This is most likely the first time the framework is ready to produce
      // a frame. Ensure that we are asked for one.
      SchedulerBinding.instance.ensureVisualUpdate();
    } else {
      element._newWidget = this;
      element.markNeedsBuild();
    }
    return element;
  }

第一次的element 肯定为null,所以执行createElement方法创建Element(也就是RenderObjectToWidgetElement),真正执行构建树的操作是 owner.buildScope方法(BuildOwner这个类来实现树的构建的)

void buildScope(Element context, [ VoidCallback callback ]) {
//_dirtyElements用来保存哪些Element需要重新渲染,第一次的时候_dirtyElements肯定为null
    if (callback == null && _dirtyElements.isEmpty)
      return;
 //
    Timeline.startSync('Build', arguments: timelineWhitelistArguments);
    try {
      _scheduledFlushDirtyElements = true;
      if (callback != null) {
       
        Element debugPreviousBuildTarget;
        
        _dirtyElementsNeedsResorting = false;
        try {
          //将根元素下的所有子元素都生成Elment元素
          callback();
        } finally {
         
        }
      }
	  //藏数据排序,根据树的深度,最上层深度为0依次类推
      _dirtyElements.sort(Element._sort);
      _dirtyElementsNeedsResorting = false;
      int dirtyCount = _dirtyElements.length;
      int index = 0;
      while (index < dirtyCount) {
      
        try {
		//需要重新渲染的元素执行rebuild
          _dirtyElements[index].rebuild();
        } catch (e, stack) {
          _debugReportException(
            ErrorDescription('while rebuilding dirty elements'),
            e,
            stack,
            informationCollector: () sync* {
              yield DiagnosticsDebugCreator(DebugCreator(_dirtyElements[index]));
              yield _dirtyElements[index].describeElement('The element being rebuilt at the time was index $index of $dirtyCount');
            },
          );
        }
        index += 1;
        if (dirtyCount < _dirtyElements.length || _dirtyElementsNeedsResorting) {
          _dirtyElements.sort(Element._sort);
          _dirtyElementsNeedsResorting = false;
          dirtyCount = _dirtyElements.length;
          while (index > 0 && _dirtyElements[index - 1].dirty) {
            // It is possible for previously dirty but inactive widgets to move right in the list.
            // We therefore have to move the index left in the list to account for this.
            // We don't know how many could have moved. However, we do know that the only possible
            // change to the list is that nodes that were previously to the left of the index have
            // now moved to be to the right of the right-most cleaned node, and we do know that
            // all the clean nodes were to the left of the index. So we move the index left
            // until just after the right-most clean node.
            index -= 1;
          }
        }
      }
   
    } finally {
      for (Element element in _dirtyElements) {
       
        element._inDirtyList = false;
      }
      _dirtyElements.clear();
      _scheduledFlushDirtyElements = false;
      _dirtyElementsNeedsResorting = null;
      Timeline.finishSync();
    
    }
    assert(_debugStateLockLevel >= 0);
  }

这个方法的作用第一是执行方法的回调,也就是调用//执行根element.mount(null, null);方法,第二是看脏数据集合中是否保存有需要重新渲染的元素,有就遍历执行element的重新rebuild方法,第一次进入肯定是不存在藏数据的,所以最终开始执行根元素的element.mount方法,如下:

  void mount(Element parent, dynamic newSlot) {
    assert(parent == null);
    super.mount(parent, newSlot);
    //创新构造
    _rebuild();
  }
 void mount(Element parent, dynamic newSlot) {
    super.mount(parent, newSlot);
    //在初始化的时候就创建了RenderObject
    _renderObject = widget.createRenderObject(this);
    assert(() {
      //rebulid的时候断言下执行_debugUpdateRenderObjectOwner,错误将直接停止运行
      _debugUpdateRenderObjectOwner();
      return true;
    }());
    assert(_slot == newSlot);
    attachRenderObject(newSlot);
    _dirty = false;
  }

根元素会调用自身 super.mount来创建RenderObject类(实现真正渲染操作的一系列子类的父类),然后调用attachRenderObject实现RenderObject链表树的插入

void attachRenderObject(dynamic newSlot) {
    assert(_ancestorRenderObjectElement == null);
    //newSlot==null
    _slot = newSlot;
    _ancestorRenderObjectElement = _findAncestorRenderObjectElement();
    //将renderobject插入链表中,好在渲染的时候遍历链表调用
    _ancestorRenderObjectElement?.insertChildRenderObject(renderObject, newSlot);
    //寻找此元素的父类元素
    final ParentDataElement<RenderObjectWidget> parentDataElement = _findAncestorParentDataElement();
    if (parentDataElement != null)
      _updateParentData(parentDataElement.widget);
  }

此时,根元素完成了自己RenderObject创建和添加到链表树之后,开始执行_rebuild()方法,

void _rebuild() {
    try {
//开始创建孩子的元素
      _child = updateChild(_child, widget.child, _rootChildSlot);
      assert(_child != null);
    } catch (exception, stack) {
      final FlutterErrorDetails details = FlutterErrorDetails(
        exception: exception,
        stack: stack,
        library: 'widgets library',
        context: ErrorDescription('attaching to the render tree'),
      );
      FlutterError.reportError(details);
      final Widget error = ErrorWidget.builder(details);
      _child = updateChild(null, error, _rootChildSlot);
    }
  }
Element updateChild(Element child, Widget newWidget, dynamic newSlot) {
    //假如newWidget等于null,则加入不活跃集合
    if (newWidget == null) {
      if (child != null)
        deactivateChild(child);
      return null;
    }
    if (child != null) {
      if (child.widget == newWidget) {
        if (child.slot != newSlot)
          updateSlotForChild(child, newSlot);
        return child;
      }
      if (Widget.canUpdate(child.widget, newWidget)) {
        if (child.slot != newSlot)
          updateSlotForChild(child, newSlot);
        child.update(newWidget);
        assert(child.widget == newWidget);
        assert(() {
          child.owner._debugElementWasRebuilt(child);
          return true;
        }());
        return child;
      }
      deactivateChild(child);
      assert(child._parent == null);
    }
    //根控件的child第一次进来的时候=null,创建所有子组件的元素并和父类相互绑定
    return inflateWidget(newWidget, newSlot);
  }

第一次的时候子元素肯定为null,所以直接执行inflateWidget方法创建子元素

Element inflateWidget(Widget newWidget, dynamic newSlot) {
    final Key key = newWidget.key;
//如果你未小部件设置了GlobalKey,那么旧的小部件将会执行不活跃的方法,新的小部件将执行活跃的方法
    if (key is GlobalKey) {
      final Element newChild = _retakeInactiveElement(key, newWidget);
      if (newChild != null) {
        //子元素关联父元素,并将元素插入到onwerbuild集合中管理
        newChild._activateWithParent(this, newSlot);
        final Element updatedChild = updateChild(newChild, newWidget, newSlot);
        assert(newChild == updatedChild);
        return updatedChild;
      }
    }
    final Element newChild = newWidget.createElement();
    newChild.mount(this, newSlot);
    return newChild;
  }

这个方法会让子Widget创建Element然后子Element调用mount创建RenderObject添加到渲染树中,从小部件的根部一层一层的调用直到叶子节点实现view树的构建。

那么构建完了之后将会调用scheduleWarmUpFrame方法实现真正的渲染操作

void scheduleWarmUpFrame() {
    if (_warmUpFrame || schedulerPhase != SchedulerPhase.idle)
      return;

    _warmUpFrame = true;
    Timeline.startSync('Warm-up frame');
    final bool hadScheduledFrame = _hasScheduledFrame;
    // We use timers here to ensure that microtasks flush in between.
    Timer.run(() {
      assert(_warmUpFrame);
      handleBeginFrame(null);
    });
    Timer.run(() {
      assert(_warmUpFrame);
      //开始渲染
      handleDrawFrame();
      // We call resetEpoch after this frame so that, in the hot reload case,
      // the very next frame pretends to have occurred immediately after this
      // warm-up frame. The warm-up frame's timestamp will typically be far in
      // the past (the time of the last real frame), so if we didn't reset the
      // epoch we would see a sudden jump from the old time in the warm-up frame
      // to the new time in the "real" frame. The biggest problem with this is
      // that implicit animations end up being triggered at the old time and
      // then skipping every frame and finishing in the new time.
      resetEpoch();
      _warmUpFrame = false;
      if (hadScheduledFrame)
        scheduleFrame();
    });

真正实现渲染的是handleDrawFrame这个方法

void handleDrawFrame() {
    assert(_schedulerPhase == SchedulerPhase.midFrameMicrotasks);
    Timeline.finishSync(); // end the "Animate" phase
    try {
      // PERSISTENT FRAME CALLBACKS
      _schedulerPhase = SchedulerPhase.persistentCallbacks;
      //在渲染的RendererBinding中加了_handlePersistentFrameCallback回调用来渲染所有的控件测量和布局
      for (FrameCallback callback in _persistentCallbacks)
        _invokeFrameCallback(callback, _currentFrameTimeStamp);

      // POST-FRAME CALLBACKS
      _schedulerPhase = SchedulerPhase.postFrameCallbacks;
      final List<FrameCallback> localPostFrameCallbacks =
          List<FrameCallback>.from(_postFrameCallbacks);
      _postFrameCallbacks.clear();
      for (FrameCallback callback in localPostFrameCallbacks)
        _invokeFrameCallback(callback, _currentFrameTimeStamp);
    } finally {
      _schedulerPhase = SchedulerPhase.idle;
      Timeline.finishSync(); // end the Frame
      assert(() {
        if (debugPrintEndFrameBanner)
          debugPrint('▀' * _debugBanner.length);
        _debugBanner = null;
        return true;
      }());
      _currentFrameTimeStamp = null;
    }
  }

这个方法里只实现了渲染的回调方法,当然你也可以自己加入一些回调方法来执行一些操作,比如打印什么的,而上面已经说了,在RendererBinding初始化的时候向回调方法中加了一个回调方法,如下

//开始渲染
  void _handlePersistentFrameCallback(Duration timeStamp) {
    drawFrame();
  }

而真正的渲染将从这里开始

 void drawFrame() {
    assert(renderView != null);
    //确定布局的位置
    pipelineOwner.flushLayout();
    pipelineOwner.flushCompositingBits();
    //开始执行画画
    pipelineOwner.flushPaint();
    renderView.compositeFrame(); // this sends the bits to the GPU
    pipelineOwner.flushSemantics(); // this also sends the semantics to the OS.
  }
void flushLayout() {
    if (!kReleaseMode) {
      Timeline.startSync('Layout', arguments: timelineWhitelistArguments);
    }
    assert(() {
      _debugDoingLayout = true;
      return true;
    }());
    try {
      // TODO(ianh): assert that we're not allowing previously dirty nodes to redirty themselves
      while (_nodesNeedingLayout.isNotEmpty) {
        //得到所有需要布局的RenderObject
        final List<RenderObject> dirtyNodes = _nodesNeedingLayout;
        _nodesNeedingLayout = <RenderObject>[];
        for (RenderObject node in dirtyNodes..sort((RenderObject a, RenderObject b) => a.depth - b.depth)) {
          if (node._needsLayout && node.owner == this)
            node._layoutWithoutResize();
        }
      }
    } finally {
      assert(() {
        _debugDoingLayout = false;
        return true;
      }());
      if (!kReleaseMode) {
        Timeline.finishSync();
      }
    }
  }

这个方法主要用来遍历树上的所有的RenderObject去执行,需要重新渲染的将会执行_layoutWithoutResize方法实现测量和确定view的位置,而第一层的node是RenderView,_layoutWithoutResize方法如下

void _layoutWithoutResize() {
    RenderObject debugPreviousActiveLayout;
    try {
      performLayout();
      markNeedsSemanticsUpdate();
    } catch (e, stack) {
      _debugReportException('performLayout', e, stack);
    }
    _needsLayout = false;
    // 为重画做准备
    markNeedsPaint();
  }

布局方法最终执行到performLayout

void performLayout() {
    assert(_rootTransform != null);
    _size = configuration.size;
    assert(_size.isFinite);

    if (child != null)
      child.layout(BoxConstraints.tight(_size));
  }

来确定子控件的位置,最终执行如下

void layout(Constraints constraints, { bool parentUsesSize = false }) {
//如果子view的大小需要父控件来确定则执行performResize方法
    if (sizedByParent) {
      try {
        performResize();
      
      } catch (e, stack) {
        _debugReportException('performResize', e, stack);
      }

    }
    RenderObject debugPreviousActiveLayout;

    try {
	//执行子控件的位置确定
      performLayout();
      markNeedsSemanticsUpdate();
   
    } catch (e, stack) {
      _debugReportException('performLayout', e, stack);
    }
    _needsLayout = false;
    markNeedsPaint();
  }
bool get sizedByParent => false;sizedByParent 默认为false,所以测量和布局都可以通过performLayout去实现,而flushPaint最终实现为
  void paint(PaintingContext context, Offset offset) { }
PaintingContext 可以得到canvas向画布上画东西,而offset确定了所画内容的坐标位置。
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值