小工具知识网

当前位置: 首页 > 行业知识 > 30分钟彻底了解Flutter整个渲染流程(超详细)

行业知识

30分钟彻底了解Flutter整个渲染流程(超详细)

2026-11-04 08:18:09 来源:互联网转载

30分钟彻底了解Flutter整个渲染流程[超详细]

  • 从运行第一行代码出发
    • WidgetsFlutterBinding初始化了一堆娃
    • 三个中流砥柱
      • SchedulerBinding
      • RendererBinding
      • WidgetsBinding
      • 申请Vsync流程
      • 下发Vsync
      • 承接Vsync

        从运行第一行代码出发

        void main() {  runApp(const MyApp());}void runApp(Widget app) {  WidgetsFlutterBinding.ensureInitialized()    ..scheduleAttachRootWidget(app)    ..scheduleWarmUpFrame();}

        WidgetsFlutterBinding.ensureInitialized作用是初始化WidgetsFlutterBinding对象。

        //... WidgetsFlutterBinding.ensureInitialized()//..static WidgetsBinding ensureInitialized() {    if (WidgetsBinding.instance == null)      WidgetsFlutterBinding();    return WidgetsBinding.instance!;  }

        WidgetsFlutterBinding初始化了一堆娃

        WidgetsFlutterBinding里面继承了BindingBase。他会初始化BindingBase的构造方法。并且

        并且with了很多类,而这些类都继承了BindingBase.也就间接对这些类进行了初始化工作

        class WidgetsFlutterBinding extends BindingBase with GestureBinding, SchedulerBinding, ServicesBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {//...}

        在BindingBase构造方法中,它调用了initInstances和initServiceExtensions。但是这里面只是做了一些debug模式一些初始化工作。由于WidgetsFlutterBinding所有with的类都是BindingBase的子类(如下图),这些子类如SchedulerBinding,RendererBinding,WidgetsBinding等他们都各自实现了initInstances, initServiceExtensions. 所以BindingBase构造函数本质是为了调用WidgetsFlutterBinding所有with的类里面的initInstances,initServiceExtensions

        abstract class BindingBase {  BindingBase() {//...    initInstances();//...    initServiceExtensions();  } @protected  @mustCallSuper  void initInstances() {    //...    //做debug模式的初始化活//...  } @protected  @mustCallSuper  void initServiceExtensions() {    //...    //做debug模式的初始化活//...  }}

        三个中流砥柱

        在这我们重点关注SchedulerBinding, RendererBinding, WidgetsBinding

        SchedulerBinding

        SchedulerBinding在这个渲染环节中主要负责请求Vsync和接收Vsync回调的工作,并且回调会消费每一帧之前的事件任务,然后进行布局绘制。后面会介绍他是怎么被执行的。(不了解什么是Vsync看看我这篇文章2分钟带你了解什么是Vsync)

        它的initInstances里面只是做了SchedulerBinding的instance单例初始化.

        mixin SchedulerBinding on BindingBase { static SchedulerBinding? get instance => _instance; static SchedulerBinding? _instance; @override  void initInstances() {    super.initInstances();    _instance = this;    //..  }@override  void initServiceExtensions() {    //...    //做debug模式的初始化活//...  }  //申请vsync void scheduleFrame() {    //...    ensureFrameCallbacksRegistered();    window.scheduleFrame(); }  //下面的CALLBACK,每一帧都会在UI绘制之前执行  void ensureFrameCallbacksRegistered() {    //执行里面scheduleFrameCallback注册的回调,动画之类的事件    window.onBeginFrame ??= _handleBeginFrame;    //执行addPersistentFrameCallback和addPostFrameCallback中注册的回调    window.onDrawFrame ??= _handleDrawFrame;  }void _handleBeginFrame(Duration rawTimeStamp) {    //...    handleBeginFrame(rawTimeStamp);  }  void _handleDrawFrame() {   //...    handleDrawFrame();  }

        RendererBinding

        RendererBinding主要是负责管理渲染的职能。

        在RendererBinding的initInstances中,他同样会初始化RendererBinding单例instance.并且初始化PipelineOwner和RenderView. 其中PipelineOwner负责管理绘制工作,RenderView是整个App的渲染树

        static RendererBinding? get instance => _instance;static RendererBinding? _instance;@overridevoid initInstances() {  super.initInstances();  _instance = this;  _pipelineOwner = PipelineOwner(      onNeedVisualUpdate: ensureVisualUpdate,      onSemanticsOwnerCreated: _handleSemanticsOwnerCreated,      onSemanticsOwnerDisposed: _handleSemanticsOwnerDisposed,    );  //...  initRenderView();  //...  addPersistentFrameCallback(_handlePersistentFrameCallback);} void initRenderView() {    //..    renderView = RenderView(configuration: createViewConfiguration(), window: window);    renderView.prepareInitialFrame(); }@overridevoid initServiceExtensions() {    //...    //做debug模式的初始化活//...}

        最后,他会调用addPersistentFrameCallback绑定绘制页面的callback.

        也就是这个_handlePersistentFrameCallback被触发时候会调用drawFrame, drawFrame这个方法是对所有被标记需要刷新的页面进行布局和绘制。 这里不展开具体绘制过程。

        void _handlePersistentFrameCallback(Duration timeStamp) {//    drawFrame();    //...} //开始绘制 void drawFrame() {   //进行布局    pipelineOwner.flushLayout();    //进行绘制    pipelineOwner.flushPaint();    //...  }

        WidgetsBinding

        WidgetsBinding主要用来挂载BuildOwner管理Element这棵树。

        他的initInstances里面会同样会初始化他的单例方法,并且会初始化携带BuildOwner。

          static WidgetsBinding? get instance => _instance;  static WidgetsBinding? _instance;  @override  void initInstances() {    super.initInstances();    _instance = this; //...    _buildOwner = BuildOwner();    //这个方法将会被scheduleBuildFor调用    buildOwner!.onBuildScheduled = _handleBuildScheduled;    //..  }  @override  void initServiceExtensions() {    //...    //做debug模式的初始化活//...  }

        BuildOwner是整棵Element的树的管理类。每个Element都会有这个BuildOwner的唯一实例。BuildOwner在WidgetsBinding绑定了onBuildScheduled方法,也就是_handleBuildScheduled, 这个方法会调用ensureVisualUpdate,然后调用SchedulerBinding的 scheduleFrame方法,从而可以申请Vsync信号,从而获取下一帧的绘制。

        onBuildScheduled这个方法将被scheduleBuildFor调用。当Element执行markNeedsBuild后就会调用BuildOwner的scheduleBuildFor调用,然后调用了onBuildScheduled。

        State->setState->markNeedsBuild->scheduleBuildFor->onBuildScheduled->ensureVisualUpdate->scheduleFrame

        _handleBuildScheduled(){//....    ensureVisualUpdate();}void ensureVisualUpdate() {    switch (schedulerPhase) {      case SchedulerPhase.idle:      case SchedulerPhase.postFrameCallbacks:        scheduleFrame();        return;      case SchedulerPhase.transientCallbacks:      case SchedulerPhase.midFrameMicrotasks:      case SchedulerPhase.persistentCallbacks:        return;    }  }

        等到下一帧到来的时候,被标记的Element就会被遍历执行渲染对象的布局和绘制。怎么被标记?看看平时的setState方法,

        State

          @protected  void setState(VoidCallback fn) {       _element!.markNeedsBuild(); } 

        Element

        BuildOwner? _owner;void markNeedsBuild() {    //...    _dirty = true;   owner!.scheduleBuildFor(this);  //..}

        BuildOwner

          void scheduleBuildFor(Element element) {  //..   //申请Vsync    onBuildScheduled!();   //..  _dirtyElements.add(element);  //    element._inDirtyList = true;   //..  }

        首先会被标记_dirty=true代表需要被更新的对象, 然后会放到_dirtyElements里面,并且标记_inDirtyList=true已经添加到element树里面

        综上所述,WidgetsFlutterBinding.ensureInitialized()做了以下几件事情:

        1. 创建个WidgetsFlutterBinding实例
        2. 初始化, SchedulerBinding, RendererBinding, WidgetsBinding 等所有单例
        3. WidgetsBinding.instance=SchedulerBinding.instance=RendererBinding.instance=WidgetsFlutterBinding()
        4. 然后执行SchedulerBinding, RendererBinding, WidgetsBinding 所有类的initInstances,initServiceExtensions方法
        5. 完成SchedulerBinding, RendererBinding, WidgetsBinding 所有相关的callback绑定,完成渲染树,Element树的管理类的初始化

        接下来我们看看…scheduleAttachRootWidget(app),做了什么

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

        scheduleAttachRootWidgets属于WidgetsBinding的方法,调用了attachRootWidget,这个方法作用是将MyApp作为Widget树的根结点绑定到_renderViewElement这个棵Element树上,并且将渲染树也绑定到上面,由于第一次执行,renderViewElement肯定是空的,所以会触发SchedulerBinding.instance!.ensureVisualUpdate(),在上面已经提过ensureVisualUpdate这个方法,这里是第一次执行,所以他会注册_handleBeginFrame,_handleDrawFrame的回调(先记住这里注册,后面会讲解怎么被系统执行的)。然后请求Vsync,将会得到下一帧的绘制回调(注意在这里,是第一帧)

        WidgetsBinding

        @protected  void scheduleAttachRootWidget(Widget rootWidget) {    Timer.run(() {      attachRootWidget(rootWidget);    });  } //这个方法将Widget,Render树都绑定在Element树上 void attachRootWidget(Widget rootWidget) {     final bool isBootstrapFrame = renderViewElement == null;    _readyToProduceFrames = true;    _renderViewElement = RenderObjectToWidgetAdapter(      container: renderView,      debugShortDescription: '[root]',      child: rootWidget,    ).attachToRenderTree(buildOwner!, renderViewElement as RenderObjectToWidgetElement?);    //如果是第一次,这里会执行    if (isBootstrapFrame) {      //由于之前执行了WidgetsFlutterBinding.ensureInitialized()      // SchedulerBinding.instance确保有实例的      // 请求下一帧绘制,也就是第一帧绘制。      SchedulerBinding.instance!.ensureVisualUpdate();    }  }

        接下来是…scheduleWarmUpFrame(),这个方法是属于SchedulerBinding,

        主要是执行了handleBeginFrame和handleDrawFrame。上面已经讲解这2个方法的作用,

        由于在RendererBinding中addPersistentFrameCallback,并且调用drawFrame方法,所以

        执行handleDrawFrame会执行drawFrame这个方法,从而会布局和绘制页面。

        void scheduleWarmUpFrame() { //... handleBeginFrame(null); //... handleDrawFrame();} //执行里面scheduleFrameCallback注册的回调,动画之类的事件void handleBeginFrame(Duration? rawTimeStamp) {//...   try {      // TRANSIENT FRAME CALLBACKS      _frameTimelineTask?.start('Animate', arguments: timelineArgumentsIndicatingLandmarkEvent);      _schedulerPhase = SchedulerPhase.transientCallbacks;      final Map callbacks = _transientCallbacks;      _transientCallbacks = {};      callbacks.forEach((int id, _FrameCallbackEntry callbackEntry) {        if (!_removedIds.contains(id))          _invokeFrameCallback(callbackEntry.callback, _currentFrameTimeStamp!, callbackEntry.debugStack);      });      _removedIds.clear();    } finally {      _schedulerPhase = SchedulerPhase.midFrameMicrotasks;    }}//执行addPersistentFrameCallback和addPostFrameCallback中注册的回调// void handleDrawFrame() {  //...      _schedulerPhase = SchedulerPhase.persistentCallbacks;      for (final FrameCallback callback in _persistentCallbacks)        _invokeFrameCallback(callback, _currentFrameTimeStamp!);      // POST-FRAME CALLBACKS      _schedulerPhase = SchedulerPhase.postFrameCallbacks;      final List localPostFrameCallbacks =          List.of(_postFrameCallbacks);      _postFrameCallbacks.clear();      for (final FrameCallback callback in localPostFrameCallbacks)        _invokeFrameCallback(callback, _currentFrameTimeStamp!);  //.. }

        综上所述,scheduleAttachRootWidget和 scheduleWarmUpFrame做如下几件事情:

        1. 将所有树绑定关联起来
        2. 设置好Vsync信号下一帧回来执行的回调(后面验证 window是在哪里被执行的)

          window.onBeginFrame ??= _handleBeginFrame;

          window.onDrawFrame ??= _handleDrawFrame;

        3. 请求获取第一帧的绘制
        4. 强制执行handleBeginFrame和 handleDrawFrame

        感叹,普普通通的一行runApp,会触发这么多业务,如果不是细细品尝,很难发现这些关系。

        上面讲的都是如何申请Vsync,然后被动触发事件任务的执行,还有布局的绘制工作,那么接下来需要串通的是,如何给Vsync发出申请,然后原生App怎么下发Vsync给Flutter下发执行的

        申请Vsync流程

        我们回头看上面提到的scheduleFrame会请求Vsync这个方法,最终会执行window.scheduleFrame, scheduleFrame是在window.dart这个类里

        mixin SchedulerBinding on BindingBase{//..  void scheduleFrame() {//..    ensureFrameCallbacksRegistered();    window.scheduleFrame();   //...  }//..}

        window其实是FlutterWindow的引用

        class FlutterWindow extends FlutterView {//...@overridefinal PlatformDispatcher platformDispatcher;void scheduleFrame() => platformDispatcher.scheduleFrame();//...}

        对于PlatformDispatcher的scheduleFrame,是调用了native方法

        class PlatformDispatcher{  void scheduleFrame() native 'PlatformConfiguration_scheduleFrame';}

        接下来,我们来到flutter引擎源码, lib/ui/window/platform_configuration.cc.

        他执行的是PlatformConfigurationNativeApi::ScheduleFrame.他调用了

        PlatformConfigurationClient的ScheduleFrame方法

        PlatformConfigurationClient* client_;PlatformConfigurationClient* client() const { return client_; }//void PlatformConfigurationNativeApi::ScheduleFrame() {  UIDartState::ThrowIfUIOperationsProhibited();  UIDartState::Current()->platform_configuration()->client()->ScheduleFrame();}

        而PlatformConfigurationClient是由RuntimeController实现的,代码在runtime/runtime_controller.cc

        class RuntimeController : public PlatformConfigurationClient   RuntimeDelegate& client_;}void RuntimeController::ScheduleFrame() {  //  client_.ScheduleFrame();}

        接着是用RuntimeDelegate进行申请,也就是引擎类,他在shell/common/engine.h这个路径,

        他会调用animator_的RequestFrame,这个才是最终真正进行申请的类

        class Engine final : public blink::RuntimeDelegate{//..Animator& animator_;//..}void Engine::ScheduleFrame(bool regenerate_layer_tree) {  animator_->RequestFrame(regenerate_layer_tree);}

        代码在shell/common/animator.cc

        void Animator::RequestFrame(bool regenerate_layer_tree) {  //......  task_runners_.GetUITaskRunner()->PostTask(//......       frame_request_number = frame_request_number_]() {        //......        //申请Vsync        self->AwaitVSync();      });}

        我们下面来看看Animator的源码AwaitVSync,

        class Animator final {  std::shared_ptr waiter_;}void Animator::AwaitVSync() {  waiter_->AsyncWaitForVsync(      [self = weak_factory_.GetWeakPtr()](          std::unique_ptr frame_timings_recorder) {        //...        self->BeginFrame(std::move(frame_timings_recorder));//...      });}

        他是委托VsyncWaiter实现,文件在shell/common/vsync_waiter.cc

        void VsyncWaiter::AsyncWaitForVsync(const Callback& callback) {  //......  callback_ = std::move(callback);  //......  AwaitVSync();}

        然后点 AwaitVSync进去发现, 是空实现。头大了,找了很久发现是在shell/platform/android/vsync_waiter_android.cc里面实现的.也就是他对应在安卓的VsyncWaiterAndroid::AwaitVSync源码中

        void VsyncWaiterAndroid::AwaitVSync() {  //......  task_runners_.GetPlatformTaskRunner()->PostTask([java_baton]() {    JNIEnv* env = fml::jni::AttachCurrentThread();    env->CallStaticVoidMethod(        g_vsync_waiter_class->obj(),          //调用安卓的asyncWaitForVsync        g_async_wait_for_vsync_method_,        java_baton    );  });}

        VsyncWaiterAndroid::AwaitVSync它会调用安卓的Java文件FlutterJNI.java的静态方法

        asyncWaitForVsync

        asyncWaitForVsyncDelegate是个接口

         public interface AsyncWaitForVsyncDelegate {    void asyncWaitForVsync(final long cookie); }

        让我们看看他的实现类

        // TODO(mattcarroll): add javadoc.public class VsyncWaiter {private final FlutterJNI.AsyncWaitForVsyncDelegate asyncWaitForVsyncDelegate =      new FlutterJNI.AsyncWaitForVsyncDelegate() {        @Override        public void asyncWaitForVsync(long cookie) {          Choreographer.getInstance()              .postFrameCallback(                  new Choreographer.FrameCallback() {                    @Override                    public void doFrame(long frameTimeNanos) {                      long delay = System.nanoTime() - frameTimeNanos;                      if (delay  

        ,好家伙,原来他是用Choreographer.getInstance().postFrameCallback

        这个方法会触发申请Vsync,然后收到Vsync会回调这个new Choreographer.FrameCallback.

        也就是说,等Vsync下发回来的时候执行Choreographer.FrameCallback的doFrame方法。

        不了解Android中Choreographer的朋友,可以阅读我这篇文章点击>>15分钟带你彻底了解App绘制流程-安卓篇

        找到申请Vsync后,下一步就是把Vsync发到Flutter,执行下一帧的工作

        下发Vsync

        在Choreographer.FrameCallback的doFrame执行中,会调用 flutterJNI.onVsync,在这你已经猜到了,这里开始要下发Vsync给flutter了, 于是上面的waiter_->AsyncWaitForVsync就会执行回调,也就是

        //...self->BeginFrame(std::move(frame_timings_recorder));//...

        让我们一路往下看

        void Animator::BeginFrame(    std::unique_ptr frame_timings_recorder) {  //...  delegate_.OnAnimatorBeginFrame(frame_target_time, frame_number);  //...}void Shell::OnAnimatorBeginFrame(fml::TimePoint frame_target_time,                                 uint64_t frame_number) {  //...  if (engine_) {    engine_->BeginFrame(frame_target_time, frame_number);  }}void Engine::BeginFrame(fml::TimePoint frame_time, uint64_t frame_number) {  //..  runtime_controller_->BeginFrame(frame_time, frame_number);}

        runtime_controller_在上面讲过,他是PlatformConfiguration的实例。

        我们现在需要去flutter看看一个文件ui.dart

        里面part了hooks.dart, 而hooks里面声明了很多被c++调用的代码其中有

        @pragma('vm:entry-point')void _drawFrame() {  PlatformDispatcher.instance._drawFrame();}@pragma('vm:entry-point')void _beginFrame(int microseconds, int frameNumber) {  PlatformDispatcher.instance._beginFrame(microseconds);  PlatformDispatcher.instance._updateFrameData(frameNumber);}

        PlatformConfiguration中,有个方法将hooks的方法做了关联,关联了_beginFrame,和_drawFrame,也就是PlatformConfiguration可以调用这个2个方法,如下

        void PlatformConfiguration::DidCreateIsolate() {  Dart_Handle library = Dart_LookupLibrary(tonic::ToDart("dart:ui"));  //...  begin_frame_.Set(tonic::DartState::Current(),                   Dart_GetField(library, tonic::ToDart("_beginFrame")));  draw_frame_.Set(tonic::DartState::Current(),                  Dart_GetField(library, tonic::ToDart("_drawFrame")));  //...}

        承接Vsync

        接着来看看runtime_controller_的方法BeginFrame, 你会发现,这个方法其实就是调用了

        hooks.dart的_beginFrame和_drawFrame方法

        void PlatformConfiguration::BeginFrame(fml::TimePoint frameTime,                                       uint64_t frame_number) {  //......  tonic::LogIfError(      tonic::DartInvoke(begin_frame_.Get(), {          Dart_NewInteger(microseconds),          Dart_NewInteger(frame_number),      }));  UIDartState::Current()->FlushMicrotasksNow();  tonic::LogIfError(tonic::DartInvokeVoid(draw_frame_.Get()));}

        _beginFrame和_drawFrame这两个方法是被由PlatformDispatcher持有的。

        接着回头看看上面提到的ensureFrameCallbacksRegistered这个方法,

          void ensureFrameCallbacksRegistered() {    window.onBeginFrame ??= _handleBeginFrame;    window.onDrawFrame ??= _handleDrawFrame; }

        看看window是怎么设置onBeginFrame和onDrawFrame的

        window.dart

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

        也就是说,Vsync下发回来其实就是触发window设置的回调onBeginFrame(SchedulerBinding的_handleBeginFrame)和onDrawFrame(SchedulerBinding的_handleDrawFrame).

        这2个方法被回调就会进行事件任务的执行,以及布局绘制的工作。

        终于,到这里所有流程都串通了.

        我们重新梳理下所有流程:

        1. 先初始化SchedulerBinding,RendererBinding,WidgetsBinding单例,确保不为空
        2. RendererBinding绑定persistentFrameCallback每次收到Vsync就进行布局和绘制工作
        3. 将SchedulerBinding的_handleBeginFrame,_handleDrawFrame通过window.onBeginFrame, onDrawFrame方法被绑定到platformDispatcher
        4. 初始化三棵树的绑定
        5. 申请第一个Vsync绘制第一帧
        6. 执行native方法从而让c++代码向安卓发出请求Vsync请求并绑定回调
        7. 安卓获取到Vsync后调用JNI传递Vsync给c++, 然后c++调用SchedulerBinding的_handleBeginFrame,_handleDrawFrame从而完成一帧的工作

        好了,所以流程已经梳理完毕,是不是很赞?如果这篇文章对你有帮助,请关注,点赞,收藏三连哦

上一篇:牛客网C++专项题目整理(2)

下一篇:宝塔控制面板操作详解,轻松上手,设置修改无忧!