关于Flutter系列,本打算从main函数一路看下去,然鹅一进来就看到了这么一个类
class WidgetsFlutterBinding extends BindingBase with GestureBinding,ServicesBinding,SchedulerBinding,
PaintingBinding,SemanticsBinding,RendererBinding,WidgetsBinding {
}
这都是啥?仔细数了下不多不少7个 依次看一下
BindingBase
先看一下这个类的注释(这里我努力翻译了下,可惜自身水平实在低,感觉还不如机翻,有兴趣的老哥可以自己去看一下英语注释 TT 难为大家了
/// 提供单利服务的mixin们的基类
///
/// 使用on继承此类并实现initInstances方法 这个mixin在app的生命周期内只能被构建一次,在checked
/// 模式下会对此断言
///
/// 用于编写应用程序的最顶层将具有一个继承自[BindingBase]并使用所有各种[BindingBase]
/// mixins(例如[ServicesBinding])的具体类。
/// 比如Flutter中的Widgets库引入了一个名为[WidgetsFlutterBinding]的binding,定义了如何绑定
/// 可以隐性(例如,[WidgetsFlutterBinding]从[runApp]启动),或者需要应用程序显式调用构造函数
实际就是用来绑定flutter引擎,我们看一下代码(因为本文只为了简单了解过程,所以会省略一些断言或者其他代码~
abstract class BindingBase {
BindingBase() {
initInstances();//在构造函数里进行初始化
initServiceExtensions();//初始化扩展服务
}
ui.Window get window => ui.window;//提供window
@protected
@mustCallSuper
void initInstances() {//初始化,其他binding mixin可以重写此类
}
@protected
@mustCallSuper
void initServiceExtensions() {}//用于子类重写该方法,用于注册一些扩展服务。
@protected
bool get locked => _lockCount > 0;
int _lockCount = 0;
@protected
Future<void> lockEvents(Future<void> callback()) {
_lockCount += 1;
final Future<void> future = callback();
future.whenComplete(() {
_lockCount -= 1;
if (!locked) {
unlocked();
}
});
return future;
}
@protected
@mustCallSuper
void unlocked() {//解锁
assert(!locked);
}
Future<void> reassembleApplication() {//代码更改时用于重绘页面
return lockEvents(performReassemble);
}
@mustCallSuper
@protected
Future<void> performReassemble() {//重绘方法,需要重写
return Future<void>.value();
}
···省略一些服务扩展方法
}
好,基类就先看到这里,接下来我们按照顺序看看其他类
GestureBinding
看名字就知道,手势相关 直接看代码,类注释也说明了,用来绑定手势系统
mixin GestureBinding on BindingBase implements HitTestable, HitTestDispatcher, HitTestTarget {//关于继承的命中测试相关类,我们暂时不去关注
@override
void initInstances() {//初始化,关联window
super.initInstances();
_instance = this;
window.onPointerDataPacket = _handlePointerDataPacket;//给window添加事件回调
}
@override
void unlocked() {
super.unlocked();
_flushPointerEventQueue();//刷新事件队列
}
static GestureBinding get instance => _instance;
static GestureBinding _instance;//单例 因为binding都是单例,以后的代码就不展示了
final Queue<PointerEvent> _pendingPointerEvents = Queue<PointerEvent>();//事件队列
void _handlePointerDataPacket(ui.PointerDataPacket packet) {//处理事件
_pendingPointerEvents.addAll(PointerEventConverter.expand(packet.data, window.devicePixelRatio));//先放入事件队列
if (!locked)
_flushPointerEventQueue();//刷新缓存的事件队列
}
void _flushPointerEventQueue() {
assert(!locked);
while (_pendingPointerEvents.isNotEmpty)//依次处理缓存的事件
_handlePointerEvent(_pendingPointerEvents.removeFirst());
}
//具体的分发逻辑我们暂时不分析了
@override // from HitTestDispatcher
void dispatchEvent(PointerEvent event, HitTestResult hitTestResult) {
//分发事件
}
}
下面看第二个
ServicesBinding
监听平台消息并将它们定向到[defaultBinaryMessenger]。
同时也提供了LICENSE文件的缓存等功能(其实讲道理我不知道这个LICENSE是啥,有老哥说明一下么TT
接下来
SchedulerBinding
看名字就知道,和任务调度相关 先看一下它的注释
/// 用于调度以下内容的运行:
///
/// * _临时回调_,由系统的[Window.onBeginFrame]触发回调,用于将应用程序的行为同步到系统
/// 显示 例如,[Ticker]和[AnimationController]的触发器。
///
/// * _Persistent callbacks_,(持续回掉)由系统的[Window.onDrawFrame]触发回调,用于在临
/// 时回掉后执行此回掉更新系统的显示 例如渲染层使用它驱动渲染管道
///
/// * _Post-frame callbacks_,在持久回调之后运行,在从[Window.onDrawFrame]回调返回之前。
///
/// * 非渲染任务,在帧之间运行。 它们具有优先级,并根据[schedulingStrategy]按优先级顺序执行。
下面看一下初始化代码
void initInstances() {
super.initInstances();
_instance = this;
window.onBeginFrame = _handleBeginFrame;//给window添加了两个回调
window.onDrawFrame = _handleDrawFrame;
SystemChannels.lifecycle.setMessageHandler(_handleLifecycleMessage);//添加了处理生命周期的回调
readInitialLifecycleStateFromNativeWindow();
···
}
这个任务调度的类有以下几个属性
enum SchedulerPhase {
idle,//空闲状态
transientCallbacks,//临时回调正在运行
midFrameMicrotasks,//临时回调结束,此状态下会运行一些Microtasks的任务
persistentCallbacks,//持久回调(handleDrawFrame调用,进行构建/布局/绘制
postFrameCallbacks,//清理,安排下一帧
}
下面我们按照类注释中标示的几种任务依次查看,首先就是由window.onBeginFrame触发的临时回掉
由初始化的方法可以知道,onBeginFrame会调用这里的_handleBeginFrame
void _handleBeginFrame(Duration rawTimeStamp) {
handleBeginFrame(rawTimeStamp);
}
//处理
void handleBeginFrame(Duration rawTimeStamp) {
...省略代码
assert(schedulerPhase == SchedulerPhase.idle);//当前是不是空闲阶段
_hasScheduledFrame = false;
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;//结束临时回掉状态
}
}
//调用一个临时回掉
void _invokeFrameCallback(FrameCallback callback, Duration timeStamp, [ StackTrace callbackStack ]) {
···
try {
callback(timeStamp);//使用时间戳调用callback
}
···
}
下面就是_handleDrawFrame方法 从名字上就知道,在里面我们绘制一帧图片
void _handleDrawFrame() {
handleDrawFrame();
}
void handleDrawFrame() {
assert(_schedulerPhase == SchedulerPhase.midFrameMicrotasks);//是否完成了临时回掉状态
Timeline.finishSync(); // end the "Animate" phase
try {
// PERSISTENT FRAME CALLBACKS
_schedulerPhase = SchedulerPhase.persistentCallbacks;//进入持续回掉状态
for (FrameCallback callback in _persistentCallbacks)
_invokeFrameCallback(callback, _currentFrameTimeStamp);//先对_persistentCallbacks中的回掉循环处理
// POST-FRAME CALLBACKS//然后再运行之前提到的post-frame回掉
_schedulerPhase = SchedulerPhase.postFrameCallbacks;//进入postFrame回掉状态
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;
}
}
由此可见,schedulerbinding在一帧的绘制中,并不负责如何绘制,只负责掌握绘制方法的调用时机
下面我们再看一下这个方法
void ensureVisualUpdate() {//确保视图更新
switch (schedulerPhase) {
case SchedulerPhase.idle:
case SchedulerPhase.postFrameCallbacks:
scheduleFrame();//如果当前是闲置或者postFrame状态
return;
case SchedulerPhase.transientCallbacks:
case SchedulerPhase.midFrameMicrotasks:
case SchedulerPhase.persistentCallbacks:
return;
}
}
void scheduleFrame() {//调用此方法后,引擎就会调用handleBeginFrame来开始我们一帧的绘制了
if (_hasScheduledFrame || !_framesEnabled)
return;
assert(() {
if (debugPrintScheduleFrameStacks)
debugPrintStack(label: 'scheduleFrame() called. Current phase is $schedulerPhase.');
return true;
}());
window.scheduleFrame();
_hasScheduledFrame = true;
}
另外还有一个方法
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();
});
// Lock events so touch events etc don't insert themselves until the
// scheduled frame has finished.
lockEvents(() async {
await endOfFrame;
Timeline.finishSync();
});
}
上面这个方法是立刻手动调用了绘制一帧的操作,用于系统启动时调用
还有一些其他的任务调度等,我们就先不关注了,感兴趣的同学可以自己去看看
接下来看下一个
PaintingBinding
mixin PaintingBinding on BindingBase, ServicesBinding {//这里可以看到,它同时需要ServicesBinding
@override
void initInstances() {
super.initInstances();
_instance = this;
_imageCache = createImageCache();//建立图像缓存
if (shaderWarmUp != null) {
shaderWarmUp.execute();//运行预热着色器
}
}
static ShaderWarmUp shaderWarmUp = const DefaultShaderWarmUp();//默认着色器的预热
@protected
ImageCache createImageCache() => ImageCache();
/// Calls through to [dart:ui] with [decodedCacheRatioCap] from [ImageCache].
Future<ui.Codec> instantiateImageCodec(Uint8List list) {
return ui.instantiateImageCodec(list);
}
@override
void evict(String asset) {
super.evict(asset);
imageCache.clear();
}
}
这一块代码不是很多,简单看一下,不深入分析了,以后再讲
然后按照顺序来就是
SemanticsBinding
mixin SemanticsBinding on BindingBase {
static SemanticsBinding get instance => _instance;
static SemanticsBinding _instance;
@override
void initInstances() {
super.initInstances();
_instance = this;
_accessibilityFeatures = window.accessibilityFeatures;//辅助功能相关
}
@protected
void handleAccessibilityFeaturesChanged() {
_accessibilityFeatures = window.accessibilityFeatures;
}
ui.AccessibilityFeatures get accessibilityFeatures => _accessibilityFeatures;
ui.AccessibilityFeatures _accessibilityFeatures;
bool get disableAnimations {
bool value = _accessibilityFeatures.disableAnimations;
assert(() {
if (debugSemanticsDisableAnimations != null)
value = debugSemanticsDisableAnimations;
return true;
}());
return value;
}
}
下面把关注点放在接下来的两个类上
已经分析了5/7,是不是要结束了?错❌,才刚刚开始,我觉得接下来的这两个类距离我们就比较近了,值得好好看看
RendererBinding
听名字就知道了,和渲染相关
我们看先一下它的初始化
void initInstances() {
super.initInstances();
_instance = this;
_pipelineOwner = PipelineOwner(
onNeedVisualUpdate: ensureVisualUpdate,
onSemanticsOwnerCreated: _handleSemanticsOwnerCreated,
onSemanticsOwnerDisposed: _handleSemanticsOwnerDisposed,
);//生成了一个pipelineOwner的实例,并给他添加了相关的回掉
window
..onMetricsChanged = handleMetricsChanged
..onTextScaleFactorChanged = handleTextScaleFactorChanged
..onPlatformBrightnessChanged = handlePlatformBrightnessChanged
..onSemanticsEnabledChanged = _handleSemanticsEnabledChanged
..onSemanticsAction = _handleSemanticsAction;
//给window设置了回掉
initRenderView();//初始化renderView
_handleSemanticsEnabledChanged();//处理语意改变
assert(renderView != null);
addPersistentFrameCallback(_handlePersistentFrameCallback);//在这里给我们之前看到的持续回掉任务队列添加了一个任务
_mouseTracker = _createMouseTracker();//鼠标追踪
}
上面涉及了几个类和方法,我们先依次看一下 首先就是
PipeLineOwner
先看看注释
/// 用于管理渲染中的管道
/// 提供了接口用于驱动渲染管道,并在管道的每个阶段存储渲染对象请求访问的状态。要刷新管道,请按顺序调
/// 用以下函数:
///
/// 1. [flushLayout]更新需要计算布局的渲染对象。在此阶段,计算每个渲染对象的大小和位置。此阶段渲
/// 染对象可能会弄脏他们的绘画或合成状态
/// 2. [flushCompositingBits]更新具有脏合成位的任何渲染对象。在此阶段,每个渲染对象都会了解其子
/// 项是否需要合成。在绘制阶段使用此信息时,选择如何实现剪裁等视觉效果。如果渲染对象具有合成子对象,
/// 则需要使用[Layer图层]创建剪辑,以便将剪辑应用于合成子对象(将其绘制到自己的[图层]中)。
/// 3. [flushPaint]访问需要绘制的任何渲染对象。在此阶段,渲染对象有机会将绘制命令记录到
/// [PictureLayer]并构造其他合成的[Layer]。
/// 4.最后,如果启用了语义,[flushSemantics]将编译渲染对象的语义。辅助技术使用该语义信息来改进渲
/// 染树的可访问性。
简单说,就是渲染对象的绘制管理类,我们看一下注释提到的几个方法//省略了一些断言和其他代码
void flushLayout() {
while (_nodesNeedingLayout.isNotEmpty) {
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();//对需要layout的渲染对象按照深度依次测量布局
}
}
}
void flushCompositingBits() {
_nodesNeedingCompositingBitsUpdate.sort((RenderObject a, RenderObject b) => a.depth - b.depth);
for (RenderObject node in _nodesNeedingCompositingBitsUpdate) {
if (node._needsCompositingBitsUpdate && node.owner == this)
node._updateCompositingBits();//对需要更新合成的对象依次更新
}
_nodesNeedingCompositingBitsUpdate.clear();
}
void flushPaint() {
final List<RenderObject> dirtyNodes = _nodesNeedingPaint;
_nodesNeedingPaint = <RenderObject>[];
// Sort the dirty nodes in reverse order (deepest first).
for (RenderObject node in dirtyNodes..sort((RenderObject a, RenderObject b) => b.depth - a.depth)) {
assert(node._layer != null);
if (node._needsPaint && node.owner == this) {
if (node._layer.attached) {
PaintingContext.repaintCompositedChild(node);//调用此方法绘制节点,这里从叶子节点开始
} else {
node._skippedPaintingOnLayer();
}
}
}
assert(_nodesNeedingPaint.isEmpty);
}
下面看一下初始化RenderView的相关操作 因为目的是了解流程,我同样省略了一些断言判断等代码
RenderView
RendererBinding====
void initRenderView() {
assert(renderView == null);
renderView = RenderView(configuration: createViewConfiguration(), window: window);
renderView.scheduleInitialFrame();//初始化Frame
}
//上面这段代码中,renderView = RenderView(configuration: createViewConfiguration(), window: window);并不是简单的赋值,实际上RendererBinding重写了renderView的set方法,我们看
set renderView(RenderView value) {
assert(value != null);
_pipelineOwner.rootNode = value;//可以看到这里实际上是把生成的RenderView设置给pipeLineOwner。而pipelineOwner的rootNode 的set方法也是设置的
}
set rootNode(AbstractNode value) {
if (_rootNode == value)
return;
_rootNode?.detach();
_rootNode = value;
_rootNode?.attach(this);//可以看到,调用了renderView的attach方法 这个方法具体我们下两篇再说
}
RenderView====
void scheduleInitialFrame() {
assert(owner != null);
assert(_rootTransform == null);
scheduleInitialLayout();//提交初始化布局的任务
scheduleInitialPaint(_updateMatricesAndCreateNewRootLayer());//提交初始化绘制的任务
assert(_rootTransform != null);
owner.requestVisualUpdate();//请求视图更新
}
void scheduleInitialLayout() {
owner._nodesNeedingLayout.add(this);//把自己添加到管道管理者需要布局的list中,前面见到过
}
void scheduleInitialPaint(ContainerLayer rootLayer) {
_layer = rootLayer;
owner._nodesNeedingPaint.add(this);//加入需要绘制的list中
}
PipeLineOwner====
void requestVisualUpdate() {
if (onNeedVisualUpdate != null)
onNeedVisualUpdate();//这个回调正是binding初始化的时候传进来的,实际上这个方法并不是RendererBinding实现的,而是SchedulerBinding实现的,我们之前看过了,这个方法就会触发一帧的开始
}
最后我们看一下添加给任务调度的一个回调方法
_handlePersistentFrameCallback
void _handlePersistentFrameCallback(Duration timeStamp) {
drawFrame();//直接调用了drawframe方法
}
drawFrame
其实这个方法的代码并不多,先看一下
@protected
void drawFrame() {
assert(renderView != null);
pipelineOwner.flushLayout();//刷新布局
pipelineOwner.flushCompositingBits();//刷新组合
pipelineOwner.flushPaint();//刷新绘制
renderView.compositeFrame(); // 发送bit给Gpu
pipelineOwner.flushSemantics(); // this also sends the semantics to the OS.
}
可以看到实际上就是按照pipelineowner要求的顺序刷新绘制
对这个渲染binding的分析目前就到这里,我们来看最后一个类
WidgetsBinding
顾名思义,就是widget层和Flutter引擎的binding 看了这么多的类,终于遇到widget了,说好的一切都是widget呢哈哈
首先还是看一下初始化方法
void initInstances() {
super.initInstances();
_instance = this;
buildOwner.onBuildScheduled = _handleBuildScheduled;//给buildOwner添加回掉
window.onLocaleChanged = handleLocaleChanged;
window.onAccessibilityFeaturesChanged = handleAccessibilityFeaturesChanged;//添加回掉
SystemChannels.navigation.setMethodCallHandler(_handleNavigationInvocation);
SystemChannels.system.setMessageHandler(_handleSystemMessage);
}
可以看到主要就是添加了一些回掉,我们分析一下
首先我们看到了buildOwner,这是啥呢
BuildOwner
还是先看一下注释
/// widgets框架的管理者
/// 此类跟踪需要重构的widgets 并处理其他适用于widget tree整体的任务,例如管理树的非活动元素列表,并在调试时在
/// 热重新加载期间触发“重组”命令。
///
/// 主buildowner通常由WidgetsBinding拥有,并且由操作系统随构建/布局/绘制管道以外的部分驱动。
///
/// 可以构建额外的buildowner用来管理屏幕以外的widget tree
///
/// 要将buildowner分配给树,请在widget树的root上使用[RootRenderObjectElement.assignOwner]方法。
然后我们看看他有啥功能
先是构造函数
BuildOwner({ this.onBuildScheduled });//没啥特别的,可以传一个回掉
下面我们来看两个比较长的函数
1.buildscope
首先看看注释,看他是干啥的
建立更新widget树的范围并回调callback(如果有的话),然后按照深度顺序构建通过scheduleBuildFor方法标记为dirty的element。
这种机制可以防止构造方法递归导致死循环
在`callback`返回后处理dirty列表。如果在此方法运行时元素被标记为脏,则它们必须比`context`节点更深,并且比此传递中的任何先前构建的节点更深。
要刷新当前的dirty列表单不执行其他操作,可以调用此方法但不传callbak参数。每一帧在WidgetsBinding.drawFrame中都会这么做
同一时间只能调用一次
[buildScope]也意味着[lockState]范围。
要在每次调用此方法时打印控制台消息,请将[debugPrintBuildScope]设置为true。 这在调试涉及widget没有被标记为脏的问题或者过于频繁地标记为脏时很有用。
下面我们看一下方法实现 比较长,我省略一些断言或者打印或者其他代码
void buildScope(Element context, [ VoidCallback callback ]) {
try {
_scheduledFlushDirtyElements = true;//标记正在刷新dirty元素
if (callback != null) {//如果有callback的话
Element debugPreviousBuildTarget;
_dirtyElementsNeedsResorting = false;
try {
callback();//先调用callback的方法
} finally {
···
}
}
_dirtyElements.sort(Element._sort);//按照深度排序
_dirtyElementsNeedsResorting = false;
int dirtyCount = _dirtyElements.length;
int index = 0;
while (index < dirtyCount) {//循环
···
try {
_dirtyElements[index].rebuild();//依次rebuild
} catch (e, stack) {}
index += 1;
if (dirtyCount < _dirtyElements.length || _dirtyElementsNeedsResorting) {
_dirtyElements.sort(Element._sort);
_dirtyElementsNeedsResorting = false;
dirtyCount = _dirtyElements.length;
while (index > 0 && _dirtyElements[index - 1].dirty) {//回漱查找
index -= 1;
}
}
}
···
} finally {
for (Element element in _dirtyElements) {
assert(element._inDirtyList);
element._inDirtyList = false;//放出来
}
_dirtyElements.clear();//清除
_scheduledFlushDirtyElements = false;
_dirtyElementsNeedsResorting = null;
Timeline.finishSync();
···
}
}
544
2.下一个就是前面说的用来标记element dirty的方法
///添加一个element到dirtylist中,等 WidgetsBinding.drawFrame调用buildScope时就会被重建
void scheduleBuildFor(Element element) {
if (element._inDirtyList) { //已经在了
_dirtyElementsNeedsResorting = true;//需要重新排序
return;
}
if (!_scheduledFlushDirtyElements && onBuildScheduled != null) {//没有正在刷新
_scheduledFlushDirtyElements = true;//开始刷新
onBuildScheduled();//回掉
}
_dirtyElements.add(element);//添加进去
element._inDirtyList = true;
}
下面我们就看看WeigetsBinding的回掉onBuildScheduled
void _handleBuildScheduled() {
ensureVisualUpdate();//调用SchedulerBinding的刷新视图任务
}
3.看一下释放tree
void finalizeTree() {
try {
lockState(() {
_inactiveElements._unmountAll(); //在这里把所有不活跃状态的element卸载
});
}
接下来我们看一下前面多次提到的drawFrame
drawFrame
看名字就知道,这里开始绘制一帧,实际上在前面我们已经见过这个方法了,就是在RendererBinding中,这里对它惊醒了重写。那么是哪里调用的呢?大家可能忘了,还是在是在之前的rendererBinding中
void _handlePersistentFrameCallback(Duration timeStamp) {
drawFrame();//开始绘制这一帧
}
那我们看一下这个方法的代码
@override
void drawFrame() {
if (renderViewElement != null)
buildOwner.buildScope(renderViewElement);//建立更新跟节点的范围
super.drawFrame();//调用RendererBinding开始绘制一帧
buildOwner.finalizeTree();//卸载没用的element
}
attachRootWidget
void attachRootWidget(Widget rootWidget) {//通过此方法添加了根节点
_renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
container: renderView,
debugShortDescription: '[root]',
child: rootWidget,
).attachToRenderTree(buildOwner, renderViewElement);
}
好,到此为止~整个Binding们基本看完了,我们最后梳理一下合体之后初始化的整体
WidgetsFlutterBinding
首先我们回顾一下他的继承顺序
class WidgetsFlutterBinding extends BindingBase with GestureBinding, ServicesBinding, SchedulerBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {
按照顺序花了一张流程图,分析的不对的话还希望有老哥帮忙指出