前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Flutter init

Flutter init

原创
作者头像
CatEatFish
修改2020-07-09 14:25:11
1.6K0
修改2020-07-09 14:25:11
举报
文章被收录于专栏:干活分享干活分享

main()方法是dart主入口,我们从入口开始看flutter从开始加载到绘制完成都做了那些事情。

代码语言:txt
复制
//主入口
void main() => runApp(MyApp());

void runApp(Widget app) {
  WidgetsFlutterBinding.ensureInitialized()
    ..attachRootWidget(app)
    ..scheduleWarmUpFrame();
}

可以看到在runApp()方法中调用了三个方法:

  1. WidgetsFlutterBinding.ensureInitialized() , WidgetsFlutterBinding是绑定flutter框架与Flutter engine的桥梁;
  2. attachRootWidget() 将 widget 视图添加到 widget 树根部;
  3. scheduleWarmUpFrame() 渲染界面;

接下来逐个分析

1.Binding

代码语言:txt
复制
// 1.WidgetsFlutterBinding绑定flutter框架与Flutter engine的桥梁
// 2.可以看到WidgetsFlutterBinding 继承了多个binding
class WidgetsFlutterBinding extends BindingBase with GestureBinding, ServicesBinding, SchedulerBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {
  static WidgetsBinding ensureInitialized() {
    if (WidgetsBinding.instance == null)
      WidgetsFlutterBinding();
      // 3.返回 WidgetsBinding
    return WidgetsBinding.instance;
  }
}

WidgetsFlutterBinding继承了多个binding 如手势,服务,线程,绘制 ...

在初始化过程中,会这些binding会完成各自的初始化,并添加回掉,在渲染流程中分别回调各自的方法,这里先不详细讲解。

通过ensureInitialized方法会返回一个 WidgetsBinding 单例对象;

继续往下看

2. attach

代码语言:txt
复制
  //
  void attachRootWidget(Widget rootWidget) {
  //把rootWidget 传进去通过 RenderObjectToWidgetAdapter 创建一个渲染视图的元素 _renderViewElement
    _renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
      container: renderView,
      //用来描述部件的(忽略)
      debugShortDescription: '[root]',
       //树根部小部件
      child: rootWidget,
      //添加到渲染树上
    ).attachToRenderTree(buildOwner, renderViewElement);
  }
  
   RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement<T> element ]) {
    if (element == null) {
      owner.lockState(() {
        //会重新调用创建方法,创建新的element
        element = createElement();
        assert(element != null);
        element.assignOwner(owner);
      });
      owner.buildScope(element, () {
        //执行rebuild()
        element.mount(null, null);
      });
    } else {
      element._newWidget = this;
      element.markNeedsBuild();
    }
    return element;
  }

首先会创建一个渲染视图的元素,然后通过attachToRenderTree()添加到树上,最好执行mount() 去渲染,接下来我们看看它是如何更新渲染的

代码语言:txt
复制
class RenderObjectToWidgetElement<T extends RenderObject> extends RootRenderObjectElement {

@override
  void mount(Element parent, dynamic newSlot) {
    assert(parent == null);
    super.mount(parent, newSlot);
    //执行rebuild
    _rebuild();
  }
  void _rebuild() {
    try {
      _child = updateChild(_child, widget.child, _rootChildSlot);
      assert(_child != null);
    } 
    ...
}
Element updateChild(Element child, Widget newWidget, dynamic newSlot) {
    ...
    if (newWidget == null) {
      if (child != null)
        // 1.newWidget为 Null,child not Null,移除child
        deactivateChild(child);
        // 2.newWidget为 Null,child  Null,返回null
      return null;
    }
    if (child != null) {
      if (child.widget == newWidget) {
        if (child.slot != newSlot)
          updateSlotForChild(child, newSlot);
        return child;
      }
      //3.是否更新child
      if (Widget.canUpdate(child.widget, newWidget)) {
        if (child.slot != newSlot)
          updateSlotForChild(child, newSlot);
          //更新child
        child.update(newWidget);
        assert(child.widget == newWidget);
        assert(() {
          child.owner._debugElementWasRebuilt(child);
          return true;
        }());
        return child;
      }
      deactivateChild(child);
      assert(child._parent == null);
    }
    return inflateWidget(newWidget, newSlot);
  }
  

主要对原始child做添加,删除,更新操作;

newWidget == null

newWidget != null

child == null

Returns null.

Returns new Element.

child != null

Old child is removed, returns null.

Old child updated if possible, returns child or new Element.

3. schedule

接下来看第三个方法scheduleWarmUpFrame()

代码语言:txt
复制
 void scheduleWarmUpFrame() {
    ...
    Timer.run(() {
    // 1.处理Transient回调函数
      handleBeginFrame(null);
    });
    Timer.run(() {
      handleDrawFrame();
      //是否需要再次调度
      if (hadScheduledFrame)
        scheduleFrame();
    });
  }

scheduleWarmUpFrame()用来渲染界面,主要包括两个方法

1. handleBeginFrame(null)处理临时回调函数

代码语言:txt
复制
void handleBeginFrame(Duration rawTimeStamp) {
    ...
    try {
      // TRANSIENT FRAME CALLBACKS
      Timeline.startSync('Animate', arguments: timelineWhitelistArguments);
      _schedulerPhase = SchedulerPhase.transientCallbacks;
      final Map<int, _FrameCallbackEntry> callbacks = _transientCallbacks;
      _transientCallbacks = <int, _FrameCallbackEntry>{};
      callbacks.forEach((int id, _FrameCallbackEntry callbackEntry) {
        if (!_removedIds.contains(id))
          _invokeFrameCallback(callbackEntry.callback, _currentFrameTimeStamp, callbackEntry.debugStack);
      });
      _removedIds.clear();
    } finally {
      _schedulerPhase = SchedulerPhase.midFrameMicrotasks;
    }
  }

这个函数主要是在依次回调”Transient“函数,这些回调函数是在调度之前设置在SchedulerBinding里的,这里的“Transient”意思是临时的,或者说是一次性的。原因是这些回调函数只会被调用一次。注意看代码里_transientCallbacks被置为空Map了。如果想在下一帧再次调用的话需要提前重新设置回调。这些回调主要和动画有关系。

在运行回调之前_schedulerPhase的状态被设置为SchedulerPhase.transientCallbacks。回调处理完以后状态更新至SchedulerPhase.midFrameMicrotasks意思是接下来会处理微任务队列。处理完微任务以后,engine会接着回调onDrawFrame()。

2. handleDrawFrame()

代码语言:txt
复制
void handleDrawFrame() {
  
    try {
      // PERSISTENT FRAME CALLBACKS
      _schedulerPhase = SchedulerPhase.persistentCallbacks;
      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;
    }
  }

在handleDrawFrame里按顺序处理了两类回调,一类叫“Persistent”回调,另一类叫“Post-Frame”回调。

“Persistent”字面意思是永久的。这类回调一旦注册以后是不能取消的。主要用来驱动渲染流水线。渲染流水线的构建(build),布局(layout)和绘制(paint)阶段都是在其中一个回调里的。

“Post-Frame”回调主要是在新帧渲染完成以后的一类调用,此类回调只会被调用一次。

在运行“Persistent”回调之前_schedulerPhase状态变为SchedulerPhase.persistentCallbacks。在运行“Post-Frame”回调之前_schedulerPhase状态变为SchedulerPhase.postFrameCallbacks。最终状态变为SchedulerPhase.idle。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.Binding
  • 2. attach
  • 3. schedule
    • 1. handleBeginFrame(null)处理临时回调函数
      • 2. handleDrawFrame()
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档