main()方法是dart主入口,我们从入口开始看flutter从开始加载到绘制完成都做了那些事情。
//主入口
void main() => runApp(MyApp());
void runApp(Widget app) {
WidgetsFlutterBinding.ensureInitialized()
..attachRootWidget(app)
..scheduleWarmUpFrame();
}
可以看到在runApp()方法中调用了三个方法:
接下来逐个分析
// 1.WidgetsFlutterBinding绑定flutter框架与Flutter engine的桥梁
// 2.可以看到WidgetsFlutterBinding 继承了多个binding
class WidgetsFlutterBinding extends BindingBase with GestureBinding, ServicesBinding, SchedulerBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {
static WidgetsBinding ensureInitialized() {
if (WidgetsBinding.instance == null)
WidgetsFlutterBinding();
// 3.返回 WidgetsBinding
return WidgetsBinding.instance;
}
}
WidgetsFlutterBinding继承了多个binding 如手势,服务,线程,绘制 ...
在初始化过程中,会这些binding会完成各自的初始化,并添加回掉,在渲染流程中分别回调各自的方法,这里先不详细讲解。
通过ensureInitialized方法会返回一个 WidgetsBinding 单例对象;
继续往下看
//
void attachRootWidget(Widget rootWidget) {
//把rootWidget 传进去通过 RenderObjectToWidgetAdapter 创建一个渲染视图的元素 _renderViewElement
_renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
container: renderView,
//用来描述部件的(忽略)
debugShortDescription: '[root]',
//树根部小部件
child: rootWidget,
//添加到渲染树上
).attachToRenderTree(buildOwner, renderViewElement);
}
RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement<T> element ]) {
if (element == null) {
owner.lockState(() {
//会重新调用创建方法,创建新的element
element = createElement();
assert(element != null);
element.assignOwner(owner);
});
owner.buildScope(element, () {
//执行rebuild()
element.mount(null, null);
});
} else {
element._newWidget = this;
element.markNeedsBuild();
}
return element;
}
首先会创建一个渲染视图的元素,然后通过attachToRenderTree()添加到树上,最好执行mount() 去渲染,接下来我们看看它是如何更新渲染的
class RenderObjectToWidgetElement<T extends RenderObject> extends RootRenderObjectElement {
@override
void mount(Element parent, dynamic newSlot) {
assert(parent == null);
super.mount(parent, newSlot);
//执行rebuild
_rebuild();
}
void _rebuild() {
try {
_child = updateChild(_child, widget.child, _rootChildSlot);
assert(_child != null);
}
...
}
Element updateChild(Element child, Widget newWidget, dynamic newSlot) {
...
if (newWidget == null) {
if (child != null)
// 1.newWidget为 Null,child not Null,移除child
deactivateChild(child);
// 2.newWidget为 Null,child Null,返回null
return null;
}
if (child != null) {
if (child.widget == newWidget) {
if (child.slot != newSlot)
updateSlotForChild(child, newSlot);
return child;
}
//3.是否更新child
if (Widget.canUpdate(child.widget, newWidget)) {
if (child.slot != newSlot)
updateSlotForChild(child, newSlot);
//更新child
child.update(newWidget);
assert(child.widget == newWidget);
assert(() {
child.owner._debugElementWasRebuilt(child);
return true;
}());
return child;
}
deactivateChild(child);
assert(child._parent == null);
}
return inflateWidget(newWidget, newSlot);
}
主要对原始child做添加,删除,更新操作;
newWidget == null | newWidget != null | |
---|---|---|
child == null | Returns null. | Returns new Element. |
child != null | Old child is removed, returns null. | Old child updated if possible, returns child or new Element. |
接下来看第三个方法scheduleWarmUpFrame()
void scheduleWarmUpFrame() {
...
Timer.run(() {
// 1.处理Transient回调函数
handleBeginFrame(null);
});
Timer.run(() {
handleDrawFrame();
//是否需要再次调度
if (hadScheduledFrame)
scheduleFrame();
});
}
scheduleWarmUpFrame()用来渲染界面,主要包括两个方法
void handleBeginFrame(Duration rawTimeStamp) {
...
try {
// TRANSIENT FRAME CALLBACKS
Timeline.startSync('Animate', arguments: timelineWhitelistArguments);
_schedulerPhase = SchedulerPhase.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 {
_schedulerPhase = SchedulerPhase.midFrameMicrotasks;
}
}
这个函数主要是在依次回调”Transient“函数,这些回调函数是在调度之前设置在SchedulerBinding里的,这里的“Transient”意思是临时的,或者说是一次性的。原因是这些回调函数只会被调用一次。注意看代码里_transientCallbacks被置为空Map了。如果想在下一帧再次调用的话需要提前重新设置回调。这些回调主要和动画有关系。
在运行回调之前_schedulerPhase的状态被设置为SchedulerPhase.transientCallbacks。回调处理完以后状态更新至SchedulerPhase.midFrameMicrotasks意思是接下来会处理微任务队列。处理完微任务以后,engine会接着回调onDrawFrame()。
void handleDrawFrame() {
try {
// PERSISTENT FRAME CALLBACKS
_schedulerPhase = SchedulerPhase.persistentCallbacks;
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;
}
}
在handleDrawFrame里按顺序处理了两类回调,一类叫“Persistent”回调,另一类叫“Post-Frame”回调。
“Persistent”字面意思是永久的。这类回调一旦注册以后是不能取消的。主要用来驱动渲染流水线。渲染流水线的构建(build),布局(layout)和绘制(paint)阶段都是在其中一个回调里的。
“Post-Frame”回调主要是在新帧渲染完成以后的一类调用,此类回调只会被调用一次。
在运行“Persistent”回调之前_schedulerPhase状态变为SchedulerPhase.persistentCallbacks。在运行“Post-Frame”回调之前_schedulerPhase状态变为SchedulerPhase.postFrameCallbacks。最终状态变为SchedulerPhase.idle。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有