当前位置:网站首页>56. Core principle of flutter - flutter startup process and rendering pipeline
56. Core principle of flutter - flutter startup process and rendering pipeline
2022-06-27 11:53:00 【Wind and rain 「 83 」】
start-up
Flutter The entrance of is in "lib/main.dart" Of main() Function , It is Dart The starting point of the application . stay Flutter Application ,main() The simplest implementation of the function is as follows :
void main() => runApp(MyApp()); You can see main() The function only calls one runApp() Method , Let's see. runApp() What has been done in the method :
void runApp(Widget app) {
WidgetsFlutterBinding.ensureInitialized()
..attachRootWidget(app)
..scheduleWarmUpFrame();
}
Parameters app It's a widget, It is Flutter The first component to be displayed after application startup . and WidgetsFlutterBinding It's the binding widget The framework and Flutter The bridge of the engine , The definition is as follows :
class WidgetsFlutterBinding extends BindingBase with GestureBinding, ServicesBinding, SchedulerBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {
static WidgetsBinding ensureInitialized() {
if (WidgetsBinding.instance == null)
WidgetsFlutterBinding();
return WidgetsBinding.instance;
}
}
You can see WidgetsFlutterBinding Inherited from BindingBase And mixed with a lot of Binding, Here are some of these Binding Let's introduce Window, Here is Window Official explanation :
The most basic interface to the host operating system's user interface.
Obviously ,Window It is Flutter Framework Interface to host operating system . Let's see Window Partial definition of class :
class Window {
// Of the current device DPI, That is, how many physical pixels a logical pixel displays , The greater the number , The more detailed the display effect, the more fidelity .
// DPI Is the firmware property of the device screen , Such as Nexus 6 The screen of DPI by 3.5
double get devicePixelRatio => _devicePixelRatio;
// Flutter UI The size of the drawing area
Size get physicalSize => _physicalSize;
// The default language of the current system Locale
Locale get locale;
// Current system font scaling .
double get textScaleFactor => _textScaleFactor;
// When the drawing area size changes, the callback
VoidCallback get onMetricsChanged => _onMetricsChanged;
// Locale Changes occur, callback
VoidCallback get onLocaleChanged => _onLocaleChanged;
// System font scaling change callback
VoidCallback get onTextScaleFactorChanged => _onTextScaleFactorChanged;
// Callback before drawing , It is usually affected by the vertical synchronization signal of the monitor VSync drive , It is called when the screen refreshes
FrameCallback get onBeginFrame => _onBeginFrame;
// Draw callback
VoidCallback get onDrawFrame => _onDrawFrame;
// Click or pointer event callback
PointerDataPacketCallback get onPointerDataPacket => _onPointerDataPacket;
// Dispatch Frame, After the method is executed ,onBeginFrame and onDrawFrame Will be called immediately at the right time ,
// This method directly calls Flutter engine Of Window_scheduleFrame Method
void scheduleFrame() native 'Window_scheduleFrame';
// Update application in GPU Rendering on , This method directly calls Flutter engine Of Window_render Method
void render(Scene scene) native 'Window_render';
// Send platform message
void sendPlatformMessage(String name,
ByteData data,
PlatformMessageResponseCallback callback) ;
// Platform channel message processing callback
PlatformMessageCallback get onPlatformMessage => _onPlatformMessage;
... // Other properties and callbacks
}
You can see Window Class contains some information about the current device and system, as well as Flutter Engine Some callbacks for . Now let's come back and see WidgetsFlutterBinding Mixed with all kinds of Binding. By looking at these Binding Source code , We can find these Binding It is basically listening and processing Window Some events of the object , Then these events are classified as Framework Model packaging 、 Abstract and then distribute . You can see WidgetsFlutterBinding It's adhesion Flutter engine With the upper class Framework Of “ glue ”.
GestureBinding: Provideswindow.onPointerDataPacketCallback , binding Framework Gesture subsystem , yes Framework The binding entry of event model and underlying event .ServicesBinding: Provideswindow.onPlatformMessageCallback , Used to bind platform message channel (message channel), It mainly deals with primitives and Flutter signal communication .SchedulerBinding: Provideswindow.onBeginFrameandwindow.onDrawFrameCallback , Listen for refresh Events , binding Framework Drawing scheduling subsystem .PaintingBinding: Bind drawing library , Mainly used for image caching .SemanticsBinding: Semantic level and Flutter engine The bridge , It is mainly the underlying support of auxiliary functions .RendererBinding: Provideswindow.onMetricsChanged、window.onTextScaleFactorChangedWait for a callback . It is a rendering tree with Flutter engine The bridge .WidgetsBinding: Provideswindow.onLocaleChanged、onBuildScheduledWait for a callback . It is Flutter widget Layer and engine The bridge .
WidgetsFlutterBinding.ensureInitialized() Responsible for initializing a WidgetsBinding Global example of , And then I call WidgetsBinding Of attachRootWidget Method , This method is responsible for the root Widget Add to RenderView On , The code is as follows :
void attachRootWidget(Widget rootWidget) {
_renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
container: renderView,
debugShortDescription: '[root]',
child: rootWidget
).attachToRenderTree(buildOwner, renderViewElement);
}
Be careful , In the code are renderView and renderViewElement Two variables ,renderView It's a RenderObject, It is the root of the rendering tree , and renderViewElement yes renderView Corresponding Element object , It can be seen that this method mainly completes the root widget To the root RenderObject To the root Element The whole association process of . Let's see. attachToRenderTree Source code implementation :
RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [RenderObjectToWidgetElement<T> element]) {
if (element == null) {
owner.lockState(() {
element = createElement();
assert(element != null);
element.assignOwner(owner);
});
owner.buildScope(element, () {
element.mount(null, null);
});
} else {
element._newWidget = this;
element.markNeedsBuild();
}
return element;
}
This method is responsible for creating the root element, namely RenderObjectToWidgetElement, And will element And widget Association , The is created widget The tree corresponds to element Trees . If element It has been created , Then root element Relating to widget Set to new , From this we can see that element It will only be created once , It will be reused later . that BuildOwner What is it? ? In fact, he is widget framework Management category , What does it track widget Need to rebuild .
The component tree is building (build) After the completion of , go back to runApp In the implementation of , When the call is finished attachRootWidget after , The last line calls WidgetsFlutterBinding Example of scheduleWarmUpFrame() Method , This method is implemented in SchedulerBinding in , It will draw once immediately after it is called , Before the end of this drawing , This method locks the event distribution , That is, before the end of this drawing Flutter Will not respond to various events , This ensures that new redraws will not be triggered during the drawing process .
Render pipeline
Frame
A drawing process , We call it a frame (frame). What we said before Flutter Can achieve 60fps(Frame Per-Second) It means that one second can trigger at most 60 Second redraw ,FPS The bigger the value is. , The smoother the interface . Here's the thing to note Flutter in Of frame The concept is not equivalent to screen refresh frames (frame), because Flutter UI Framework of the frame Not every screen refresh triggers , This is because , If UI Remain unchanged for a period of time , It is not necessary to go through the rendering process again every time the screen is refreshed , therefore ,Flutter After the first frame is rendered, an active request is taken frame Only when UI The rendering process will not be restarted until it may change .
- Flutter stay
windowRegister one ononBeginFrameAnd aonDrawFrameCallback , stayonDrawFrameThe callback will eventually calldrawFrame. - When we call
window.scheduleFrame()After method ,Flutter The engine will be at the right time ( It can be considered before the next refresh of the screen , Depending on Flutter Engine Implementation ) To callonBeginFrameandonDrawFrame.
Can see , Only active call scheduleFrame(), Will execute drawFrame. therefore , We are Flutter It is mentioned in frame when , If there is no special instruction , And drawFrame() The call of corresponds to , Instead of corresponding to the refresh rate of the screen .
Flutter Scheduling process SchedulerPhase
Flutter The application execution process is simply divided into idle and frame Two kinds of state ,idle Status means no frame Handle , If the application status changes, it needs to be refreshed UI, You have to go through scheduleFrame() To ask for new frame, When frame When we arrive , It's in frame state , Whole Flutter The application life cycle is in idle and frame Switch between the two states .
frame Processing flow
When there is a new frame When we arrive , Start calling SchedulerBinding.handleDrawFrame To deal with it frame, The specific process is to execute four task queues in turn :transientCallbacks、midFrameMicrotasks、persistentCallbacks、postFrameCallbacks, When the four task queues are completed, the current frame end .
Sum up ,Flutter Divide the whole life cycle into five states , adopt SchedulerPhase Enumerate classes to represent them :
enum SchedulerPhase {
/// Idle state , did not frame Processing . This status means that the page has not changed , There is no need to re render .
/// If the page changes , Need to call `scheduleFrame()` To request frame.
/// Be careful , An idle state simply means that there is no frame Processing , Usually micro tasks 、 Timer callback or user event callback are both
/// May be executed , Like listening tap event , After the user clicks, we onTap Callback is in idle The stage is executed .
idle,
/// perform ” temporary “ Callback task ,” temporary “ The callback task can only be executed once , It will be removed after execution ” temporary “ Task queue .
/// A typical example is that the animation callback will be executed at this stage .
transientCallbacks,
/// Some new micro tasks may be generated when performing temporary tasks , For example, when executing the first temporary task, you create a
/// Future, And this Future Before all temporary tasks are completed resolve 了 , This is the case
/// Future Your callback will be in [midFrameMicrotasks] Stage execution
midFrameMicrotasks,
/// Perform some persistent tasks ( every last frame All tasks to be performed ), Such as rendering pipeline ( structure 、 Layout 、 draw )
/// Is executed in the task queue .
persistentCallbacks,
/// At present frame It will be executed before the end postFrameCallbacks, Usually some cleaning work and
/// Request new frame.
postFrameCallbacks,
}
We need to pay attention to , The rendering pipeline we will focus on next is in persistentCallbacks Implemented in .
Render pipeline (rendering pipline)
When new frame When we arrive , Call to WidgetsBinding Of drawFrame() Method , Let's take a look at its implementation :
@override
void drawFrame() {
...// Omit irrelevant code
try {
buildOwner.buildScope(renderViewElement); // Do the build first
super.drawFrame(); // Then call the parent class. drawFrame Method
}
}
In fact, the key code is just two lines : First rebuild (build), And then call the drawFrame Method , We'll take the parent class's drawFrame After method expansion :
void drawFrame() {
buildOwner!.buildScope(renderViewElement!); // 1. Rebuild widget Trees
// Here is an super.drawFrame() Method
pipelineOwner.flushLayout(); // 2. Update layout
pipelineOwner.flushCompositingBits(); //3. to update “ Layer synthesis ” Information
pipelineOwner.flushPaint(); // 4. Repaint
if (sendFramesToEngine) {
renderView.compositeFrame(); // 5. Upper screen , Will draw bit Send data to GPU
...
}
}
You can see that the main work has been done 5 thing :
Rebuild widget Trees .
Update layout .
to update “ Layer synthesis ” Information .
Repaint .
Upper screen : Display the drawn product on the screen
We call the above 5 Step: rendering pipline, Chinese translation for “ Rendering pipeline ” or “ Render pipeline ”. And this of the rendering pipeline 5 The specific process of the three steps is the focus of this chapter . Now let's setState As an example, the implementation of the update process of has a general impact on the whole update process
setState Execution flow
setState After calling :
- First call the current element Of markNeedsBuild Method , Will the current element Marked as dirty .
- Then call scheduleBuildFor, Will the current element Add to piplineOwner Of dirtyElements list .
- Finally, ask for a new frame, Then a new frame:onBuildScheduled->ensureVisualUpdate->scheduleFrame() . When new frame Execute rendering pipeline when it arrives
void drawFrame() { buildOwner!.buildScope(renderViewElement!); // Rebuild widget Trees pipelineOwner.flushLayout(); // Update layout pipelineOwner.flushCompositingBits(); // Update composition information pipelineOwner.flushPaint(); // Update drawing if (sendFramesToEngine) { renderView.compositeFrame(); // Upper screen , Will draw bit Send data to GPU pipelineOwner.flushSemantics(); // this also sends the semantics to the OS. _firstFrameSent = true; } }
- Rebuild widget Trees : If dirtyElements The list is not empty , Then traverse the list , Call each element Of rebuild Method to rebuild a new widget( Trees ), Because the new widget( Trees ) Build with the new state , So it may lead to widget Layout information ( Occupied space and location ) change , If there is a change , It will be called renderObject Of markNeedsLayout Method , This method will find the parent from the current node , Until you find one relayoutBoundary The node of , It will then be added to a global nodesNeedingLayout In the list ; If the root node is not found relayoutBoundary, Then add the root node to nodesNeedingLayout In the list .
- Update layout : Traverse nodesNeedingLayout Array , For each renderObject Rearrange ( Call its layout Method ), Determine the new size and offset .layout Method is called markNeedsPaint(), The method and markNeedsLayout The function of the method is similar to , It will also find the parent from the current node , Until you find one isRepaintBoundary The attribute is true Parent node , Then add it to a global nodesNeedingPaint In the list ; Because of the root node (RenderView) Of isRepaintBoundary by true, So we'll find one . When the lookup process is completed, the buildOwner.requestVisualUpdate Method , This method will eventually call scheduleFrame(), In this method, you will first determine whether a new... Has been requested frame, If not, request a new frame.
- Update composition information : Ignore first , Later we will introduce .
- Update drawing : Traverse nodesNeedingPaint list , Call the... Of each node paint Method to redraw , The drawing process generates Layer. It needs to be explained ,flutter The results are saved in Layer Medium , That is, as long as Layer Don't release , Then the drawn results will be cached , therefore ,Layer Can span frame To cache the drawing results , Avoid unnecessary redrawing overhead .Flutter During frame drawing , encounter isRepaintBoundary by true The nodes of , Will generate a new Layer. so Layer and renderObject Not one-to-one correspondence , Parent and child nodes can share , We will verify this in a subsequent experiment . Of course , If it's a custom component , We can do it in renderObject Manually add any number of Layer, This is usually used in cached scenes where painting elements only need to be painted once and will not change later , This will be demonstrated by an example later .
- Upper screen : After drawing , What we got was a tree Layer Trees , Finally, we need to Layer The drawing information in the tree is displayed on the screen . We know Flutter Is a self - Implementing rendering engine , therefore , We need to submit the drawing information to Flutter engine, and
renderView.compositeFrameIt is the completion of this mission .
above , That is setState Call to UI More about the update process , The actual process will be more complicated , For example build It is not allowed to call again in the procedure setState Of , The framework needs some checking . Another example is in frame Will involve the scheduling of animation 、 On the screen, all Layer Add to scene (Scene) After the object , To render Scene. The reader of the above process can have an image first , We will cover in detail in the following sections .
setState The timing of implementation
setState Will trigger build, and build It's execution persistentCallbacks Stage of execution , So as long as it is not performed at this stage setState It's absolutely safe , But the granularity is too coarse , For example transientCallbacks and midFrameMicrotasks Stage , If the application state changes , The best way to do this is to simply mark the component as dirty, Instead of asking for new frame , Because the current frame It hasn't been executed yet persistentCallbacks, Therefore, it will be refreshed in the rendering pipeline of the current frame after the execution to UI. therefore ,setState After marking dirty After that, we will judge the scheduling status first , If it is idle or perform postFrameCallbacks Stage will ask for new frame :
void ensureVisualUpdate() {
switch (schedulerPhase) {
case SchedulerPhase.idle:
case SchedulerPhase.postFrameCallbacks:
scheduleFrame(); // Request new frame
return;
case SchedulerPhase.transientCallbacks:
case SchedulerPhase.midFrameMicrotasks:
case SchedulerPhase.persistentCallbacks: // Pay attention to this line
return;
}
}
The above code is OK in most cases , But if we were build Stage calls again setState There will still be problems , Because if we were build Stage calls again setState It will lead to build.... This will cause the loop to call , therefore flutter Framework found in build Phase call setState That would be wrong , Such as :
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, c) {
// build Stage cannot call setState, Will report a mistake
setState(() {
++index;
});
return Text('xx');
},
);
}An error will be reported after operation , The console will print :
==== Exception caught by widgets library ====
The following assertion was thrown building LayoutBuilder:
setState() or markNeedsBuild() called during build.
We need to pay attention to , If we were directly in build Call in setState , The code is as follows :
@override
Widget build(BuildContext context) {
setState(() {
++index;
});
return Text('$index');
}
No error will be reported after running , The reason is in the execution of build Current component dirty state ( Corresponding element in ) by true, Only build It will be set to after execution false. and setState When executing, you will first judge the current dirty value , If true Will return directly to , So there will be no misstatement .
Above we only discussed in build Phase call setState Can cause errors , In fact, throughout the build 、 Both the layout and drawing phases cannot be called synchronously setState, This is because , At these stages, call setState It is possible to request new frame, May cause a loop call , So if you want to update the application status at these stages , Can not directly call setState.
Security updates
Now we know it's build Stage cannot call setState 了 , In fact, you can't directly resynchronize the request for re layout or re drawing in both the layout phase and the drawing phase of components , The truth is the same , What is the correct way to update at these stages , We use setState For example , It can be done by :
// stay build、 Layout 、 Draw phase security updates
void update(VoidCallback fn) {
SchedulerBinding.instance!.addPostFrameCallback((_) {
setState(fn);
});
}
Be careful ,update Function should only be in frame perform persistentCallbacks When the , Other stages call directly setState that will do . because idle Status can be a special case , If stay idle State call update Words , Manual call required scheduleFrame() Request new frame, otherwise postFrameCallbacks The next frame ( Requested by other components frame ) It will not be executed until it arrives , So we can update Revise it :
void update(VoidCallback fn) {
final schedulerPhase = SchedulerBinding.instance!.schedulerPhase;
if (schedulerPhase == SchedulerPhase.persistentCallbacks) {
SchedulerBinding.instance!.addPostFrameCallback((_) {
setState(fn);
});
} else {
setState(fn);
}
}
thus , We encapsulate a that can safely update the state update function .
summary
This section describes Flutter App Main process from Startup to display to screen , The key is Flutter Rendering process of , Pictured 14-4:

It should be noted that build Process and layout Processes can be executed alternately , We are introducing LayoutBuilder It has been explained in the first section . The reader needs to have an overview of the whole rendering process , We will introduce it in detail later , But before delving into rendering pipelines , We have to learn more about Element 、BuildContext and RenderObject Three categories .
边栏推荐
- In depth analysis of error solutions and problems in dynamic loading of unity shadow and outline components
- i.mx6ull(单片机) c语言环境搭建
- FileOutputStream
- 杰理之无缝循环播放【篇】
- R语言fpc包的dbscan函数对数据进行密度聚类分析、plot函数可视化聚类图
- 15+城市道路要素分割应用,用这一个分割模型就够了!
- L'utilisation de C language 0 length Array
- 杰理之DAC输出方式设置【篇】
- R language uses GLM function to build Poisson logarithm linear regression model, processes three-dimensional contingency table data to build saturation model, uses step function to realize stepwise re
- Nvme2.0 protocol - new features
猜你喜欢

从零开始搭建物联网系统

Drive to APasS! Use Mingdao cloud to manage F1 events
![[tcapulusdb knowledge base] tcapulusdb operation and maintenance doc introduction](/img/04/b1194ca3340b23a4fb2091d1b2a44d.png)
[tcapulusdb knowledge base] tcapulusdb operation and maintenance doc introduction

Matlab exercises - create 50 rows and 50 columns of all zero matrix, all 1 matrix, identity matrix, diagonal matrix, and output the 135 element of the matrix.

C/s architecture

Safe landing practice of software supply chain under salesforce containerized ISV scenario

AutoCAD - three pruning methods

Open source model library of flying propeller industry: accelerating the development and application of enterprise AI tasks

Prevent being rectified after 00? I. The company's recruitment requires that employees cannot sue the company

【TcaplusDB知识库】TcaplusDB常规单据介绍
随机推荐
"24 of the 29 students in the class successfully went to graduate school" rushed to the hot search! Where are the remaining five?
【TcaplusDB知识库】TcaplusDB单据受理-建表审批介绍
R语言使用MASS包的polr函数构建有序多分类logistic回归模型、使用VGAM包的vglm函数对有序多分类logistic回归模型进行平行性假设作检验
Microsoft cloud technology overview
Co jump
Deep understanding of happens before principle
[tcapulusdb knowledge base] Introduction to tcapulusdb table data caching
Drive to APasS!使用明道云管理F1赛事
Four memory areas (stack, heap, global, code area)
QStyle实现自绘界面项目实战(一)
Jerry's serial port communication serial port receiving IO needs to set digital function [chapter]
飞桨产业级开源模型库:加速企业AI任务开发与应用
力扣(LeetCode)177. 第N高的薪水(2022.06.26)
【TcaplusDB知识库】TcaplusDB单据受理-事务执行介绍
C/s architecture
1. Mx6ull startup mode
LLVM系列(1)- LLVM简介
Oracle group statistics query
Wait, how do I use setmemorylimit?
Interviewer: with the for loop, why do you need foreach?