og

Flutter启动流程分析

本文我们来分析下Flutter的启动流程,首先我们从main.dart文件的main函数开始:

        
  • 1
void main() => runApp(MyApp());

main函数则调用的是runApp函数:

        
  • 1
  • 2
  • 3
  • 4
  • 5
void runApp(Widget app) { WidgetsFlutterBinding.ensureInitialized() ..scheduleAttachRootWidget(app) ..scheduleWarmUpFrame(); }

函数中有用到Dart语法中的级联运算符(..),代表的含义是WidgetsFlutterBinding.ensureInitialized()生成的对象分别调用了scheduleAttachRootWidgetscheduleWarmUpFrame这两个方法。

先概括一下这三行代码的重要作用:

  1. 生成一个Flutter Engine(C++代码)Flutter Framework(Dart代码)中间桥接对象,官方定义为胶水对象;
  2. 根据app生成一个渲染树;
  3. 绘制热身帧, 将渲染树生成的Layer图层通过Flutter Engine渲染到Flutter View上。

概括起来很简单,但是其中包含的内容是相当复杂的。我们接下来就从这三行代码入手分析一下其中具体的流程。

WidgetsFlutterBinding

WidgetsFlutterBinding类中的所有代码如下:

        
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
class WidgetsFlutterBinding extends BindingBase with GestureBinding, SchedulerBinding, ServicesBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding { // 类初始化方法 static WidgetsBinding ensureInitialized() { if (WidgetsBinding.instance == null) // 构造方法调用 WidgetsFlutterBinding(); // 返回对象WidgetsBinding return WidgetsBinding.instance!; } }

WidgetsFlutterBinding继承自BindingBase,混入了GestureBindingSchedulerBindingServicesBindingPaintingBindingSemanticsBindingRendererBindingWidgetsBinding7个mixin。这7个mixin的功能后面详解介绍。

ensureInitialized方法就是获取WidgetsBinding.instance单例的过程。由于mixin没有构造方法,所以WidgetsFlutterBinding()实际调用的是父类BindingBase的构造方法。

        
  • 1
  • 2
  • 3
  • 4
BindingBase() { // 调用initInstances initInstances(); }

WidgetsFlutterBinding混入的7个mixin都重写了initInstances()方法,所以他们的initInstances()都会被调用。最后的调用逻辑如下图所示: https://hooyim.oss-accelerate.aliyuncs.com/uploads%2F2021%2F04%2F01%2FP7OvXLfSzOPBRVJt

initInstances

通过精妙的mixin代码设计,实现了高内聚低耦合和模块职责单一,并且通过mixin依赖,实现了initInstances()方法调用的串行按执行顺序。

FlutterView

问题:为什么突兀的来介绍FlutterView对象呢?

FlutterViewFlutter EngineFlutter Framework开放的用户界面和事件的接口,可以把Flutter Framework理解为围绕FlutterView的一个处理框架。所以其重要性不言而喻。

上面WidgetsFlutterBinding混入的多个mixin主要就是处理window对象(即FlutterView对象的)的回调事件和提交渲染内容,所以我们先来介绍一下FlutterView是非常有必要的。

window对象是BindingBase的一个变量, 名字上推测他就是个单例对象:

        
  • 1
  • 2
<!-- BindingBase --> ui.SingletonFlutterWindow get window => ui.window;

ui.windowPlatformDispatcher.instancewindowId为0的主window:

        
  • 1
  • 2
<!-- window.dart --> final SingletonFlutterWindow window = SingletonFlutterWindow._(0, PlatformDispatcher.instance);

SingletonFlutterWindow的继承图谱如下:

        
  • 1
  • 2
  • 3
  • 4
<!-- window.dart --> abstract class FlutterView {} class FlutterWindow extends FlutterView {} class SingletonFlutterWindow extends FlutterWindow {}
FlutterView
        
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
abstract class FlutterView { // PlatformDispatcher get platformDispatcher; // ViewConfiguration get viewConfiguration; // double get devicePixelRatio => viewConfiguration.devicePixelRatio; // Rect get physicalGeometry => viewConfiguration.geometry; // Size get physicalSize => viewConfiguration.geometry.size; // WindowPadding get viewInsets => viewConfiguration.viewInsets; // WindowPadding get viewPadding => viewConfiguration.viewPadding; // WindowPadding get systemGestureInsets => viewConfiguration.systemGestureInsets; // WindowPadding get padding => viewConfiguration.padding; // void render(Scene scene) => _render(scene, this); void _render(Scene scene, FlutterView view) native 'PlatformConfiguration_render'; }

FlutterView有几个重要的属性和方法:

  1. PlatformDispatcherFlutterView的核心,FlutterView是对它的一层封装,是真正向Flutter Engine发送消息和得到回调的类;
  2. ViewConfigurationPlatform View的一些信息的描述,其中主要包括几个信息:
    • devicePixelRatio:物理像素和虚拟像素的比值。这个和手机有关,譬如iPhone手机可能是2或者3,Android手机就有可能是个小数,譬如3.5等。
    • geometryFlutter渲染的ViewNative platform中的位置和大小。
    • viewInsets: 各个边显示的内容和能显示内容的边距大小;譬如:没有键盘的时候viewInsets.bottom为0,当有键盘的时候键盘挡住了一些区域,键盘底下无法显示内容,所以viewInsets.bottom就变成了键盘的高度。
    • padding: 系统UI的显示区域如状态栏,这部分区域最好不要显示内容,否则有可能被覆盖了。譬如,很多iPhone顶部的刘海区域,padding.top就是其高度。
    • viewPadding:viewInsetspadding的和。 参考地址
  3. 下面的属性都是对ViewConfiguration内部属性的暴露,便于外部获取。
  4. render方法是将Flutter代码生成的渲染内容(Layer Tree生成的Scene)传递给Flutter Engine, 让GPU去渲染。

ViewConfiguration其实也是从PlatformDispatcher获取的。

FlutterWindow

FlutterWindow没有什么功能,只是封装了一个构造方法,我们不做分析,接下来我们来看看SingletonFlutterWindow的一些重要代码:

  • devicePixelRatio, physicalSize, paddingviewInsets等的变化会触发的回调onMetricsChanged

本质是转发了platformDispatcher的回调,后面的回调方法都类似。

        
  • 1
  • 2
  • 3
  • 4
VoidCallback? get onMetricsChanged => platformDispatcher.onMetricsChanged; set onMetricsChanged(VoidCallback? callback) { platformDispatcher.onMetricsChanged = callback; }
  • 手机设置的地区(如中国大陆),以及设置的地区更改后收到的回调onLocaleChanged

    Locale get locale => platformDispatcher.locale;

    VoidCallback? get onLocaleChanged => platformDispatcher.onLocaleChanged; set onLocaleChanged(VoidCallback? callback) {

            
    • 1
    platformDispatcher.onLocaleChanged%20%3D%20callback%3B

    }

  • 文字缩放倍率变化后的回调onTextScaleFactorChanged;

    VoidCallback? get onTextScaleFactorChanged => platformDispatcher.onTextScaleFactorChanged; set onTextScaleFactorChanged(VoidCallback? callback) {

            
    • 1
    platformDispatcher.onTextScaleFactorChanged%20%3D%20callback%3B

    }

  • platformBrightness变化后的回调onPlatformBrightnessChanged;

    VoidCallback? get onPlatformBrightnessChanged => platformDispatcher.onPlatformBrightnessChanged; set onPlatformBrightnessChanged(VoidCallback? callback) {

            
    • 1
    platformDispatcher.onPlatformBrightnessChanged%20%3D%20callback%3B

    }

  • Flutter Engine根据VSync发送的准备开始下一帧的回调onBeginFrame;

    FrameCallback? get onBeginFrame => platformDispatcher.onBeginFrame; set onBeginFrame(FrameCallback? callback) {

            
    • 1
    platformDispatcher.onBeginFrame%20%3D%20callback%3B

    }

  • onBeginFrame完成后,开始绘制帧的回调onDrawFrame;

    VoidCallback? get onDrawFrame => platformDispatcher.onDrawFrame; set onDrawFrame(VoidCallback? callback) {

            
    • 1
    platformDispatcher.onDrawFrame%20%3D%20callback%3B

    }

  • 用户的手势操作(点击,滑动等)的回调onPointerDataPacket;

    PointerDataPacketCallback? get onPointerDataPacket => platformDispatcher.onPointerDataPacket; set onPointerDataPacket(PointerDataPacketCallback? callback) {

            
    • 1
    platformDispatcher.onPointerDataPacket%20%3D%20callback%3B

    }

  • 收到插件发送的消息的回调onPlatformMessage;

    PlatformMessageCallback? get onPlatformMessage => platformDispatcher.onPlatformMessage; set onPlatformMessage(PlatformMessageCallback? callback) {

            
    • 1
    platformDispatcher.onPlatformMessage%20%3D%20callback%3B

    }

  • 语义的设置和修改后的回调;

    void updateSemantics(SemanticsUpdate update) => platformDispatcher.updateSemantics(update);

    VoidCallback? get onAccessibilityFeaturesChanged => platformDispatcher.onAccessibilityFeaturesChanged; set onAccessibilityFeaturesChanged(VoidCallback? callback) {

            
    • 1
    platformDispatcher.onAccessibilityFeaturesChanged%20%3D%20callback%3B

    }

总结:

FlutterView对象window本质上是对PlatformDispatcher的封装,从PlatformDispatcher获取一些界面相关信息,获取从Flutter Engine 发送来的事件,然后触发和转发相应的回调方法。

如果有想法,可以基于window实现自己的Flutter Framework

BindingBase

BindingBase的主要功能前面都已经说明,这里总结一下:

  • 构造函数调用initInstances方法,其实是为了依次调用7个mixininitInstances方法。

  • 提供了一个window单例。

    abstract class BindingBase {

            
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    BindingBase() { // 初始化 initInstances(); } // 单例window ui.SingletonFlutterWindow get window => ui.window;

    }

RendererBinding

RendererBinding的功能主要和渲染树相关。我们来看看它的重要代码:

  • initInstances初始化方法:

    void initInstances() {

            
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    super.initInstances(); _instance = this; // 1 _pipelineOwner = PipelineOwner( onNeedVisualUpdate: ensureVisualUpdate, onSemanticsOwnerCreated: _handleSemanticsOwnerCreated, onSemanticsOwnerDisposed: _handleSemanticsOwnerDisposed, ); // 2 window ..onMetricsChanged = handleMetricsChanged ..onTextScaleFactorChanged = handleTextScaleFactorChanged ..onPlatformBrightnessChanged = handlePlatformBrightnessChanged ..onSemanticsEnabledChanged = _handleSemanticsEnabledChanged ..onSemanticsAction = _handleSemanticsAction; // 3 initRenderView(); _handleSemanticsEnabledChanged(); // 4 addPersistentFrameCallback(_handlePersistentFrameCallback); // 5 initMouseTracker();

    }

  1. 生成了一个PipelineOwner对象。它的主要作用是收集需要更新的RenderObjects,然后借助RendererBinding进行UI刷新。
  2. 处理window对象的onMetricsChanged,onTextScaleFactorChanged等回调方法。
  3. initRenderView生成了一个RenderView对象renderView, 然后将renderView设置为_pipelineOwner的根节点rootNode

这个renderView是渲染树的根节点,我们的MyApp将作为它的子节点插入渲染树。先剧透一下,后面会介绍。

  1. addPersistentFrameCallback调用的是SchedulerBinding的方法, PersistentFrameCallback主要执行的是Widgetbuild / layout / paint等一系列操作。

    void addPersistentFrameCallback(FrameCallback callback) {

            
    • 1
    _persistentCallbacks.add(callback);

    }

  1. 生成一个MouseTracker对象,处理hitTestResult或者PointerAddedEventPointerRemovedEvent事件。

    void dispatchEvent(PointerEvent event, HitTestResult? hitTestResult) {

            
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    if (hitTestResult != null || event is PointerAddedEvent || event is PointerRemovedEvent) { assert(event.position != null); _mouseTracker!.updateWithEvent(event, () => hitTestResult ?? renderView.hitTestMouseTrackers(event.position)); } super.dispatchEvent(event, hitTestResult);

    }

这里是事件传递的重要方法,后面介绍GestureBinding事件传递的时候会再次见到它。

  • drawFrame绘制方法

    void drawFrame() {

            
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    // 1 pipelineOwner.flushLayout(); // 2 pipelineOwner.flushCompositingBits(); // 3 pipelineOwner.flushPaint(); // 4 if (sendFramesToEngine) { // 5 renderView.compositeFrame(); // this sends the bits to the GPU // 6 pipelineOwner.flushSemantics(); // this also sends the semantics to the OS. }

    }

  1. pipelineOwner.flushLayout是对Dirty RenderObject进行布局定位;

  2. pipelineOwner.flushCompositingBits是更新RenderObjectneedsCompositing属性,这个属性在很多情况下需要用到,譬如裁剪(Clip),旋转(Transform)等。

  3. pipelineOwner.flushPaint是在PaintingContextRenderObject进行绘制。

  4. renderView.compositeFrame方法是用SceneBuilder将前几步的绘制结果转换成一个Scene(可以理解为一帧画面)对象,然后调用windowrender方法提交给GUP去显示,代码如下:

    void compositeFrame() {

            
    • 1
    • 2
    • 3
    • 4
    • 5
    ... final ui.SceneBuilder builder = ui.SceneBuilder(); final ui.Scene scene = layer!.buildScene(builder); _window.render(scene); ...

    }

5.pipelineOwner.flushSemantics更新语义辅助信息。

SemanticsBinding

Article created at   2021/4/1 AM  in category  code ,   50  Views

Related tags: flutter

Article Address: https://www.hooyim.com/article/10