1. 动画与微任务阶段:
主要是处理动画及执行一系列微任务。具体是在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;
}
}
2. 构建阶段(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对象初始化时注册的。主要函数:persistentCallbacks
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.
}
}
在WidgetsBinding中重写了drawFrame函数。在该函数中会创建新的Widget对象替换旧的Widget对象并将不需要的Element节点从树中移除。
@override
void drawFrame() {
...
try {
//如果根结点存在,就重新构建Widget
if (renderViewElement != null)
buildOwner.buildScope(renderViewElement);
//调用RendererBinding中的drawFrame函数
super.drawFrame();
//移除不再使用的Element节点
buildOwner.finalizeTree();
} finally {...}
...
}
2.1. 重新build Widget对象:
void buildScope(Element context, [ VoidCallback callback ]) {
if (callback == null && _dirtyElements.isEmpty)
return;
try {
//“脏”节点列表需要重新排序
_scheduledFlushDirtyElements = true;
...
//将标记为“脏”的Element节点根据深度进行排序
_dirtyElements.sort(Element._sort);
_dirtyElementsNeedsResorting = false;
//标记为“脏”的Element节点数量
int dirtyCount = _dirtyElements.length;
int index = 0;
//遍历“脏”节点
while (index < dirtyCount) {
try {
//重新构建Widget,及是否复用当前Element
_dirtyElements[index].rebuild();
} catch (e, stack) {
...
}
index += 1;
//当_dirtyElements集合中的“脏”节点还未处理完毕时,又添加了新的“脏”节点
if (dirtyCount < _dirtyElements.length || _dirtyElementsNeedsResorting) {
//根据“脏”节点的深度进行排序
_dirtyElements.sort(Element._sort);
_dirtyElementsNeedsResorting = false;
dirtyCount = _dirtyElements.length;
//如果当前节点的深度比新加入的“脏”节点深度要深,则需要将处理坐标指向新加入的“脏”节点
while (index > 0 && _dirtyElements[index - 1].dirty) {
index -= 1;
}
}
}
} finally {
//清除_dirtyElements中所有节点的“脏”状态
for (Element element in _dirtyElements) {
element._inDirtyList = false;
}
_dirtyElements.clear();
_scheduledFlushDirtyElements = false;
_dirtyElementsNeedsResorting = null;
Timeline.finishSync();
}
}
_dirtyElements是一个集合,存储了所有标记为“脏”的节点。在对其中的“脏”节点进行处理时,需要首先对集合中的“脏”节点进行排序,其排序规则如下。
● 如果“脏”节点的深度不同,则按照深度进行升序排序
● 如果“脏”节点的深度相同,则会将“脏”节点放在集合的右侧,“干净”节点则在在集合的左侧。
在排序完成后,就要遍历该集合,对其中的“脏”节点进行处理。在这里调用的是rebuild函数,通过该函数,会重新创建“脏”节点下的所有Widget对象,并根据新的Widget对象来判断是否需要重用Element对象。一般只要不是增删Widget,Element对象都会被重用,从而也就会重用RenderObject对象。由于Widget是一个非常轻量级的数据结构,所以在UI更新时做到了把性能损耗降到最低。
如果_dirtyElements中的“脏”节点还未处理完毕,就又新增了“脏”节点,那么这时候就会重新排序,保证_dirtyElements集合的左侧永远是“干净”节点,右侧永远是“脏”节点。
由于rebuild

最低0.47元/天 解锁文章
1435

被折叠的 条评论
为什么被折叠?



