本篇文章意在讲解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确定了所画内容的坐标位置。