Flutter从启动到显示
Posted 小源子2016
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flutter从启动到显示相关的知识,希望对你有一定的参考价值。
目录
前言
研究启动过程,目前是理解和加深Flutter框架原理,为后续开发高性能的Flutter应用提供理论思想
一、入口代码
这句代码就是我们启动Flutter程序的入口代码
runApp(MyApp());
二、runApp
a.源码注释:Inflate the given widget and attach it to the screen.
b.我的翻译:让给定的widget膨胀并让其附加到屏幕上
c.加深理解:何为膨胀,若是是安卓开发,你会inflate一个xml,将其膨胀为view,flutter中一个widget就相当于这个xml配置,flutter通过runApp将其膨胀成element tree和renderObject tree
void runApp(Widget app) {
WidgetsFlutterBinding.ensureInitialized()
..scheduleAttachRootWidget(app)
..scheduleWarmUpFrame();
}
三、WidgetsFlutterBinding
混入了各种Binding,如同胶水一样,使得Flutter Widget 框架 和 Flutter Engin连接在一起
那具体是如何粘接的呢?请往下看
/// A concrete binding for applications based on the Widgets framework.
///
/// This is the glue that binds the framework to the Flutter engine.
class WidgetsFlutterBinding extends BindingBase with GestureBinding, ServicesBinding, SchedulerBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {
/// Returns an instance of the [WidgetsBinding]
static WidgetsBinding ensureInitialized() {
if (WidgetsBinding.instance == null)
WidgetsFlutterBinding();
return WidgetsBinding.instance;
}
}
四、BindingBase
binding中都持有了全局对象window
ui.Window get window => ui.window;
五、Window
window就是FlutterFramework连接宿主操作系统的接口,具体来说
你看看window方法里有些方法是不是有native关键字
正如在android JNI开发的时候,也有native关键字,就是用来表明这个是和一个native
函数(c/c++)对应的,这样,我们就在window中建立了和engine(skia,Dart VM)的连接工作
class Window {
// ,一个逻辑像素显示多少物理像素,数字越大,显示效果就越精细保真。
// DPI是设备屏幕的固件属性,如Nexus 6的屏幕DPI为3.5
double get devicePixelRatio => _devicePixelRatio;
// Flutter UI绘制区域的大小
Size get physicalSize => _physicalSize;
// 当前系统默认的语言Locale
Locale get locale;
// 当前系统字体缩放比例。
double get textScaleFactor => _textScaleFactor;
// 当绘制区域大小改变回调
VoidCallback get onMetricsChanged => _onMetricsChanged;
// Locale发生变化回调
VoidCallback get onLocaleChanged => _onLocaleChanged;
// 系统字体缩放变化回调
VoidCallback get onTextScaleFactorChanged => _onTextScaleFactorChanged;
// 绘制前回调,一般会受显示器的垂直同步信号VSync驱动,当屏幕刷新时就会被调用
FrameCallback get onBeginFrame => _onBeginFrame;
// 绘制回调
VoidCallback get onDrawFrame => _onDrawFrame;
// 点击或指针事件回调
PointerDataPacketCallback get onPointerDataPacket => _onPointerDataPacket;
// 调度Frame,该方法执行后,onBeginFrame和onDrawFrame将紧接着会在合适时机被调用,
// 此方法会直接调用Flutter engine的Window_scheduleFrame方法
void scheduleFrame() native 'Window_scheduleFrame';
// 更新应用在GPU上的渲染,此方法会直接调用Flutter engine的Window_render方法
void render(Scene scene) native 'Window_render';
// 发送平台消息
void sendPlatformMessage(String name,
ByteData data,
PlatformMessageResponseCallback callback) ;
// 平台通道消息处理回调
PlatformMessageCallback get onPlatformMessage => _onPlatformMessage;
... //其它属性及回调
void updateSemantics(SemanticsUpdate update) native 'Window_updateSemantics';
void setIsolateDebugName(String name) native 'Window_setIsolateDebugName';
}
六、scheduleAttachRootWidget
总之,这一步就是根据widget inflate三颗树
/// Schedules a [Timer] for attaching the root widget.
///
/// This is called by [runApp] to configure the widget tree. Consider using
/// [attachRootWidget] if you want to build the widget tree synchronously.
@protected
void scheduleAttachRootWidget(Widget rootWidget) {
Timer.run(() {
attachRootWidget(rootWidget);
});
}
void attachRootWidget(Widget rootWidget) {
_readyToProduceFrames = true;
_renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
container: renderView,
debugShortDescription: '[root]',
child: rootWidget,
).attachToRenderTree(buildOwner, renderViewElement as RenderObjectToWidgetElement<RenderBox>);
}
// Inflate this widget and actually set the resulting [RenderObject] as the
/// child of [container].
///
/// If `element` is null, this function will create a new element. Otherwise,
/// the given element will have an update scheduled to switch to this widget.
///
/// Used by [runApp] to bootstrap applications.
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);
});
// 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;
}
七.RendererBinding
在binding初始化的时候我们这里特别介绍下RenderBingd,addPersistentFrameCallback,它注册了一个持续帧回调处理函数,它本质上通过native 函数调用绘制图形,调用window.render强制 GPU刷新当前应用显示,但是它什么时候被调用呢,请继续看
mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, SemanticsBinding, HitTestable {
@override
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;
initRenderView();
_handleSemanticsEnabledChanged();
assert(renderView != null);
addPersistentFrameCallback(_handlePersistentFrameCallback);
initMouseTracker();
}
flushLayout:renderObject布局(明确大小、位置等)
flushPaint:通过canvas api调用engin skia绘制数据
compositerFrame:通过window.render通知GPU进行光栅化
void drawFrame() {
assert(renderView != null);
pipelineOwner.flushLayout();
pipelineOwner.flushCompositingBits();
pipelineOwner.flushPaint();
if (sendFramesToEngine) {
renderView.compositeFrame(); // this sends the bits to the GPU
pipelineOwner.flushSemantics(); // this also sends the semantics to the OS.
_firstFrameSent = true;
}
}
八.scheduleWarmUpFrame
热身帧的过程是
1.触发 handleBeginFrame,其实这个是window.onBeginFrame回调,接收vsync信号,为了
加快首帧速度,我们主动触发,正常应该是engin触发
2.触发handleDrawFrame(非首帧应该是engin触发),其实这个是window.onDrawFrame回调,engine告知Flutter Frame 要
绘制帧,内部则调用了帧会调监听,就包括上一步RendererBinding注册的监听
3.然后调用scheduleFrame, 实际调到了window.scheduleFrame,触发native 的Window_scheduleFrame,然后在适合的时候,engine将会调用onBeginFrame和onDrawFrame
就是通知Engin重新调度Frame(估计有变化的时候才需要通知engin重新调度帧,所以帧回调在界面不怎么变化的时候调用次数少的)
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(() {
handleDrawFrame();
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();
});
}
总结
1.首帧处理上Flutter主动触发绘制,减少等待时间
2.在分析过程中运用了Flutter四颗树知识加深了理解
参考资料:
以上是关于Flutter从启动到显示的主要内容,如果未能解决你的问题,请参考以下文章
如何从 Firebase 获取数据到 Recyclerview 中的片段?