我正在参与「启航方案」

main进口启动

  • Flutter的主进口在”lib/main.dart”的main()函数中。在Flutter使用中,main()函数最简略的完结如下:
    void main() {
      runApp(MyApp());
    }
    
  • 能够看到main()函数只调用了一个runApp()办法,runApp()办法中都做了什么:
    void runApp(Widget app) {
      //初始化操作
      WidgetsFlutterBinding.ensureInitialized()
        //页面烘托
        ..attachRootWidget(app)
        ..scheduleWarmUpFrame();
    }
    
    • 参数app是一个Widget,是Flutter使用启动后要展现的第一个Widget。
    • WidgetsFlutterBinding正是绑定widget 结构和Flutter engine的桥梁。
  • ensureInitialized()办法

    class WidgetsFlutterBinding extends BindingBase with GestureBinding, ServicesBinding, SchedulerBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {
      static WidgetsBinding ensureInitialized() {
        if (WidgetsBinding.instance == null)
          WidgetsFlutterBinding();
        return WidgetsBinding.instance;
      }
    }
    
    • 能够看到WidgetsFlutterBinding承继自BindingBase 并混入了许多Binding,在介绍Binding之前先介绍一下WindowWindow的官方解释:The most basic interface to the host operating system’s user interface.
  • Window正是Flutter Framework衔接宿主操作体系的接口。看一下Window类的部分定义:

    class Window {
      // 当时设备的DPI,即一个逻辑像素显现多少物理像素,数字越大,显现作用就越精密保真。
      // DPI是设备屏幕的固件特点,如Nexus 6的屏幕DPI为3.5 
      double get devicePixelRatio => _devicePixelRatio;
      // 制作回调  
      VoidCallback get onDrawFrame => _onDrawFrame;
      // 发送平台消息
      void sendPlatformMessage(String name,
                               ByteData data,
                               PlatformMessageResponseCallback callback) ;
      ... //其它特点及回调
    }
    
    • Window类包括了当时设备和体系的一些信息以及Flutter Engine的一些回调。现在回来看看WidgetsFlutterBinding混入的各种Binding。经过检查这些 Binding的源码,能够发现这些Binding中基本都是监听并处理Window目标的一些事情,然后将这些事情依照Framework的模型包装、抽象然后分发。能够看到WidgetsFlutterBinding正是粘连Flutter engine与上层Framework的“胶水”。
  • 看看attachToRenderTree的源码完结:
    RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [RenderObjectToWidgetElement<T> element]) {
      if (element == null) {
        ...// 代码处理
      } else {
        ...// 代码处理
      }
      return element;
    }
    
  • 该办法担任创立根element,即 RenderObjectToWidgetElement,并且将element与widget 进行相关,即创立出 widget树对应的element树。
    • 如果element 已经创立过了,则将根element 中相关的widget 设为新的,由此能够看出element 只会创立一次,后面会进行复用。那么BuildOwner是什么呢?其实他便是widget framework的管理类,它盯梢哪些widget需求从头构建。

页面烘托

  • 回到runApp的完结中,当调用完attachRootWidget后,终究一行会调用 WidgetsFlutterBinding 实例的 scheduleWarmUpFrame() 办法,该办法的完结在SchedulerBinding 中,它被调用后会当即进行一次制作(而不是等待”vsync”信号),在此次制作完毕前,该办法会确定事情分发,也便是说在本次制作完毕完结之前Flutter将不会呼应各种事情,这能够保证在制作过程中不会再触发新的重绘。
  • 下面是scheduleWarmUpFrame() 办法的部分完结(省掉了无关代码):
    void scheduleWarmUpFrame() {
      Timer.run(() {
        handleBeginFrame(null); 
      });
      Timer.run(() {
        handleDrawFrame();  
        resetEpoch();
      });
      // 确定事情
      lockEvents(() async {
        await endOfFrame;
        Timeline.finishSync();
      });
     ...
    }
    
    • 能够看到该办法中首要调用了handleBeginFrame()handleDrawFrame() 两个办法,在看这两个办法之前咱们首先了解一下Frame 和 FrameCallback 的概念:
    • Frame: 一次制作过程,咱们称其为一帧。Flutter engine受显现器笔直同步信号”VSync”的驱使不断的触发制作。咱们之前说的Flutter能够完结60fps(Frame Per-Second),便是指一秒钟能够触发60次重绘,FPS值越大,界面就越流通。
    • FrameCallback:SchedulerBinding 类中有三个FrameCallback回调行列, 在一次制作过程中,这三个回调行列会放在不同时机被执行:
        1. transientCallbacks:用于寄存一些暂时回调,一般寄存动画回调。能够经过SchedulerBinding.instance.scheduleFrameCallback 增加回调。
        1. persistentCallbacks:用于寄存一些耐久的回调,不能在此类回调中再请求新的制作帧,耐久回调一经注册则不能移除。SchedulerBinding.instance.addPersitentFrameCallback(),这个回调中处理了布局与制作工作。
        1. postFrameCallbacks:在Frame完毕时只会被调用一次,调用后会被体系移除,可由 SchedulerBinding.instance.addPostFrameCallback() 注册,留意,不要在此类回调中再触发新的Frame,这能够会导致循环改写。
    • 自行检查handleBeginFrame()handleDrawFrame() 两个办法的源码,能够发现前者首要是执行了transientCallbacks行列,而后者执行了 persistentCallbackspostFrameCallbacks 行列。

页面制作

  • 烘托和制作逻辑在RendererBinding中完结,检查其源码,发现在其initInstances()办法中有如下代码:
    void initInstances() {
      ... //省掉无关代码
      //监听Window目标的事情  
      ui.window
        ..onMetricsChanged = handleMetricsChanged
        ..onTextScaleFactorChanged = handleTextScaleFactorChanged
        ..onSemanticsEnabledChanged = _handleSemanticsEnabledChanged
        ..onSemanticsAction = _handleSemanticsAction;
      //增加PersistentFrameCallback    
      addPersistentFrameCallback(_handlePersistentFrameCallback);
    }
    
    • 看终究一行,经过addPersistentFrameCallbackpersistentCallbacks行列增加了一个回调 _handlePersistentFrameCallback:
    void _handlePersistentFrameCallback(Duration timeStamp) {
      drawFrame();
    }
    
  • 该办法直接调用了RendererBindingdrawFrame()办法

flushLayout()

  • 代码如下所示
    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();
          }
        }
      } 
    }
    
  • 源码很简略,该办法首要任务是更新了一切被标记为“dirty”的RenderObject的布局信息。首要的动作发生在node._layoutWithoutResize()办法中,该办法中会调用performLayout()进行从头布局。

flushCompositingBits()

  • 代码如下所示
    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(); //更新RenderObject.needsCompositing特点值
      }
      _nodesNeedingCompositingBitsUpdate.clear();
    }
    
  • 检查RenderObject是否需求重绘,然后更新RenderObject.needsCompositing特点,如果该特点值被标记为true则需求重绘。

flushPaint()

  • 代码如下所示
    void flushPaint() {
     ...
      try {
        final List<RenderObject> dirtyNodes = _nodesNeedingPaint; 
        _nodesNeedingPaint = <RenderObject>[];
        // 反向遍历需求重绘的RenderObject
        for (RenderObject node in 
             dirtyNodes..sort((RenderObject a, RenderObject b) => b.depth - a.depth)) {
          if (node._needsPaint && node.owner == this) {
            if (node._layer.attached) {
              // 真正的制作逻辑  
              PaintingContext.repaintCompositedChild(node);
            } else {
              node._skippedPaintingOnLayer();
            }
          }
        }
      } 
    }
    
    • 该办法进行了终究的制作,能够看出它不是重绘了一切 RenderObject,而是只重绘了需求重绘的 RenderObject。真正的制作是经过PaintingContext.repaintCompositedChild()来制作的,该办法终究会调用Flutter engine供给的Canvas API来完结制作。

compositeFrame()

  • 代码如下所示
    void compositeFrame() {
      ...
      try {
        final ui.SceneBuilder builder = ui.SceneBuilder();
        final ui.Scene scene = layer.buildScene(builder);
        if (automaticSystemUiAdjustment)
          _updateSystemChrome();
        ui.window.render(scene); //调用Flutter engine的烘托API
        scene.dispose(); 
      } finally {
        Timeline.finishSync();
      }
    }
    
  • 这个办法中有一个Scene目标,Scene目标是一个数据结构,保存终究烘托后的像素信息。
    • 这个办法将Canvas画好的Scene传给window.render()办法,该办法会直接将scene信息发送给Flutter engine,终究由engine将图像画在设备屏幕上。

终究

  • 需求留意:由于RendererBinding只是一个mixin,而with它的是WidgetsBinding,所以需求看看WidgetsBinding中是否重写该办法,检查WidgetsBindingdrawFrame()办法源码:
    @override
    void drawFrame() {
     ...//省掉无关代码
      try {
        if (renderViewElement != null)
          buildOwner.buildScope(renderViewElement); 
        super.drawFrame(); //调用RendererBinding的drawFrame()办法
        buildOwner.finalizeTree();
      } 
    }
    
  • 发现在调用RendererBinding.drawFrame()办法前会调用 buildOwner.buildScope() (非初次制作),该办法会将被标记为“dirty” 的 element 进行 rebuild()