赞
踩
相对于React Native
、Weex
等跨平台框架,Flutter
拥有自己的UI绘制体系,避免了React Native
、Weex
等跨平台框架与Native
系统的桥接,从而更好的提升了性能。
在Flutter
中,UI都是一帧一帧的绘制,但这绘制的背后都会经过如下阶段。
Widget
的大小及位置的确定。Widget
大小及位置来绘制UI。Element
树中移除无用的Element
对象及处理绘制结束回调。下面就来分析上述的各个阶段
该阶段主要是处理动画及微任务。先来看动画的处理,在使用动画时,很多时候都会添加一个回调函数来进行状态获取或数据更新,如通过addListener
、addStatusListener
等函数来添加,而这些回调函数就会在本阶段来执行。具体是在SchedulerBinding
中的handleBeginFrame
函数中实现。
void handleBeginFrame(Duration rawTimeStamp) { ... try { // TRANSIENT FRAME CALLBACKS _schedulerPhase = SchedulerPhase.transientCallbacks; //切换为transientCallbacks阶段 final Map<int, _FrameCallbackEntry> callbacks = _transientCallbacks; //清空已注册的回调函数 _transientCallbacks = <int, _FrameCallbackEntry>{}; //遍历所有注册的回调方法 callbacks.forEach((int id, _FrameCallbackEntry callbackEntry) { if (!_removedIds.contains(id)) //执行已经注册的回调函数 _invokeFrameCallback(callbackEntry.callback, _currentFrameTimeStamp, callbackEntry.debugStack); }); _removedIds.clear(); } finally { //切换为midFrameMicrotasks阶段 _schedulerPhase = SchedulerPhase.midFrameMicrotasks; } }
_invokeFrameCallback
就会调用在使用动画时注册的回调函数,这里仅执行一次。如果我们在运行时调用_invokeFrameCallback
函数的代码注释调,那么就无法获取动画的状态,从而影响动画的正确执行。
当回调函数执行完毕后,就会进入微任务阶段,在该阶段会执行一系列微任务,由于这涉及到Flutter
的异步任务体系,因此这里就不再叙述。
在上一阶段执行完毕后,就进入build阶段,在该阶段主要是重新构建标记为“脏”的Widget
节点及将需要更新的RenderObject
对象标记为“脏”。
当handleBeginFrame
函数执行完毕后,就会执行handleDrawFrame
函数,该函数在SchedulerBinding
对象初始化时会与Window
相关联,所以除第一次需要主动调用外,其他时候皆是通过Window
来调用该函数。
void handleDrawFrame() { assert(_schedulerPhase == SchedulerPhase.midFrameMicrotasks); Timeline.finishSync(); // end the "Animate" phase try { //持久帧回调,该回调会一直存在,不会移除 _schedulerPhase = SchedulerPhase.persistentCallbacks; for (FrameCallback callback in _persistentCallbacks) _invokeFrameCallback(callback, _currentFrameTimeStamp); //当前帧绘制完成回调 _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 profile(() { _profileFrameStopwatch.stop(); _profileFramePostEvent(); }); _currentFrameTimeStamp = null; } }
这里重点关注持久帧回调,该回调也是UI绘制的关键函数,是在RendererBinding
对象初始化时注册的。
mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, SemanticsBinding, HitTestable { @override void initInstances() { super.initInstances(); ... //注册持久帧回调 addPersistentFrameCallback(_handlePersistentFrameCallback); } void _handlePersistentFrameCallback(Duration timeStamp) { //绘制帧 drawFrame(); } //绘制帧 void drawFrame() { //对Widget进行测量、布局 pipelineOwner.flushLayout(); pipelineOwner.flushCompositingBits(); //对Widget进行绘制 pipelineOwner.flushPaint(); //发送数据给GPU renderView.compositeFrame(); // this sends the bits to the GPU pipelineOwner.flushSemantics(); // this also sends the semantics to the OS. } }
根据函数名可以发现并没有发现关于构建Widget
的相关函数,那么在何时构建尼?通过查看源码可以发现,在WidgetsBinding
中重写了drawFrame
函数。在该函数中会创建新的Widget
对象替换旧的Widget
对象并将不需要的Element
节点从树中移除。
@override
void drawFrame() {
...
try {
//如果根结点存在,就重新构建Widget
if (renderViewElement != null)
buildOwner.buildScope(renderViewElement);
//调用RendererBinding中的drawFrame函数
super.drawFrame();
//移除不再使用的Element节点
buildOwner.finalizeTree();
} finally {...}
...
}
Widget
对象的创建是在buildScope()
函数中实现的,这是一个非常重要的函数,具体实现如下。
void buildScope(Element context, [ VoidCallback callback ]) {
if (callback == null &a
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。