关于flutter:Flutter-绘制动机-VSYNC-流程源码全方位分析

2次阅读

共计 13006 个字符,预计需要花费 33 分钟才能阅读完成。

Flutter 系列文章连载~

  • 《Flutter Android 工程构造及应用层编译源码深入分析》
  • 《Flutter 命令实质之 Flutter tools 机制源码深入分析》
  • 《Flutter 的 runApp 与三棵树诞生流程源码剖析》
  • 《Flutter Android 端 Activity/Fragment 流程源码剖析》
  • 《Flutter Android 端 FlutterInjector 及依赖流程源码剖析》
  • 《Flutter Android 端 FlutterEngine Java 相干流程源码剖析》
  • 《Flutter Android 端 FlutterView 相干流程源码剖析》
  • 《Flutter 绘制动机 VSYNC 流程源码全方位剖析》

背景

后面系列咱们依赖 Android 平台实现剖析了端侧很多机制,然而有一个知识点始终比拟迷糊,那就是 Flutter 是怎么被触发绘制的?这个问题在网上的答案根本都说 VSYNC,然而少有人说这个 VSYNC 是怎么被关联起来的,本文就针对这个问题进行一个 Platform 到 Engine 到 Dart Framework 剖析,源码依赖 Flutter 2.2.3。

Android 平台 Java 层

还记得咱们后面系列文章剖析过的 io.flutter.embedding.engine.FlutterJNI 吗,FlutterJNI 的作用就是架起 Android 端 Java 与 Flutter Engine C/C++ 端的一座接口桥梁。记不记得过后咱们剖析 FlutterEngine 时(《Flutter Android 端 FlutterEngine Java 相干流程源码剖析》)在他的实例化过程中有这么一段调用逻辑:

-> 调用 FlutterEngine 构造方法
-> 调用 FlutterLoader 的 startInitialization(context.getApplicationContext()) 办法
-> 调用 VsyncWaiter.getInstance((WindowManager) appContext.getSystemService(Context.WINDOW_SERVICE)).init() 办法

基于下面流程,咱们把重点转向 Java 端的 VsyncWaiter 类及其 init 办法,如下:

// 过程单例实例类
public class VsyncWaiter {
  //......
  // 一个来自 engine 触发的回调
  private final FlutterJNI.AsyncWaitForVsyncDelegate asyncWaitForVsyncDelegate =
      new FlutterJNI.AsyncWaitForVsyncDelegate() {
        @Override
        public void asyncWaitForVsync(long cookie) {
          // 每逢回调回来就向 Choreographer post 一个绘制 VSYNC 申请。Choreographer.getInstance()
              .postFrameCallback(new Choreographer.FrameCallback() {
                    @Override
                    public void doFrame(long frameTimeNanos) {float fps = windowManager.getDefaultDisplay().getRefreshRate();
                      long refreshPeriodNanos = (long) (1000000000.0 / fps);
                      // 调用 FlutterJNI 的 nativeOnVsync 逻辑告诉 VSYNC
                      FlutterJNI.nativeOnVsync(frameTimeNanos, frameTimeNanos + refreshPeriodNanos, cookie);
                    }
                  });
        }
      };
  //......
  // 惟一被调用的办法
  public void init() {
    // 设置委托实例回调援用
    FlutterJNI.setAsyncWaitForVsyncDelegate(asyncWaitForVsyncDelegate);

    // 传输 fps 值给 engine
    float fps = windowManager.getDefaultDisplay().getRefreshRate();
    FlutterJNI.setRefreshRateFPS(fps);
  }
}

咱们简略看下 FlutterJNI 外面是怎么做的,如下:

@Keep
public class FlutterJNI {
  //......
  //VsyncWaiter.init 办法设置的委托回调实现就是赋值给了他
  private static AsyncWaitForVsyncDelegate asyncWaitForVsyncDelegate;
  //......
  //VsyncWaiter.init 办法中调用
  public static void setAsyncWaitForVsyncDelegate(@Nullable AsyncWaitForVsyncDelegate delegate) {asyncWaitForVsyncDelegate = delegate;}

  // 这个办法的正文明确说了被 netive engine 调用,也就是 JNI 的 C/C++ 端调用
  private static void asyncWaitForVsync(final long cookie) {if (asyncWaitForVsyncDelegate != null) {asyncWaitForVsyncDelegate.asyncWaitForVsync(cookie);
    } else {throw new IllegalStateException("An AsyncWaitForVsyncDelegate must be registered with FlutterJNI before asyncWaitForVsync() is invoked.");
    }
  }

  //java 端调用 native 实现
  public static native void nativeOnVsync(long frameTimeNanos, long frameTargetTimeNanos, long cookie);

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

对于安卓仔来说,下面代码中相熟的零碎 API 比拟多,所以咱们先回到纯 Android 平台。老司机都晓得,古代 Android 零碎至多都是基于 VSYNC 的 Double Buffer(双缓冲)机制实现绘制,而双缓冲机制背地的核心思想是让绘制和显示领有各自的图像缓冲区,也就是说 GPU 始终将实现的一帧图像数据写入到 Back Buffer,而显示器应用 Frame Buffer 数据进行显示,这样双缓冲 Frame Buffer 中的数据肯定不会存在撕裂(相似并发不平安的写),VSYNC 信号负责调度从 Back Buffer 到 Frame Buffer 的替换操作,这里并不是真正的数据 copy,理论是替换各自的内存地址,能够认为该操作是霎时实现。


看过我 Android 源码剖析系列文章或者其余网文的小伙伴肯定都晓得,Android 中有一个 ViewRootImpl,他的 mView 成员是 DecorView(实质 FrameLayout),而 DecorView 是一个 Activity 的根 View。整个界面的重绘入口都是 ViewRootImpl 类的 scheduleTraversals 办法(不懂就去看历史文章),咱们本人调用 View 的 invalidate 办法也是相似,如下:

void scheduleTraversals() {if (!mTraversalScheduled) {
        //......
        mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        //......
    }
}

下面的 mTraversalRunnable 就是调用了 Activity 中 View 树的 measure、layout、draw 进行绘制。而 mChoreographer 就是 Choreographer,在安卓平台上,Choreographer 通过 postXXX 调用 FrameDisplayEventReceiver(继承自 DisplayEventReceiver)的 nativeScheduleVsync 办法进行 VSYNC 申请;同时 Choreographer 也通过 FrameDisplayEventReceiver 的 onVsync 办法监听了零碎 VSYNC 脉冲信号,该监听办法中会触发 Choreographer 的 doFrame 办法,该办法会把咱们 post 进去的 callback 队列拿进去执行,而后将曾经执行过的 callback 进行移除。整个过程如下图:

简略总结下结论,安卓应用程序如果有绘制(包含动画)需要的话,必须向零碎框架发动 VSYNC 申请,申请在下一次 VSYNC 信号到来时绘制利用界面。

看到下面这个论断其实如果你有肯定悟性应该能猜到 Flutter 的 VSYNC 是怎么工作的了,他其实也实现了相似规范安卓绘制触发的流程,即发送 VSYNC 申请,期待下一个 VSYNC 信号到来执行 callback 回调。咱们在持续剖析前能够基于下面 VsyncWaiter 和 FlutterJNI 相干接口进行一个猜测如下:

Flutter Framework Dart 层

Android 平台 Java 层面的问题咱们都剖析结束了,通过下面 Flutter VSYNC 猜测时序图咱们晓得重点都在 Flutter Engine 外面。也就是说 Flutter Engine 调用 FlutterJNI 的 asyncWaitForVsync 办法通过安卓平台的 Choreographer 发送 VSYNC 申请,申请在安卓平台下一次 VSYNC 信号到来时通过 FlutterJNI 的 nativeOnVsync 办法向 Flutter Engine 传递绘制信号,整个过程像极了安卓 View 统管的 ViewRootImpl 实现。

咱们晓得,Flutter Engine 是 Flutter Dart Framework 与 Android 平台之间的一个桥梁,形象如下:

所以咱们基于后面 Flutter 系列剖析及下面 Android 绘制机制大胆猜想能够晓得,VSYNC 申请来自 Flutter Dart Framework,下一次 VSYNC 信号到来触发绘制也调用到了 Flutter Dart Framework,Flutter Engine 只是一个桥梁处理过程。

发动绘制 VSYNC 申请

后面咱们剖析 Flutter App Dart main 办法时有提到 scheduleWarmUpFrame 办法最终调用了 SchedulerBinding 的 scheduleFrame 办法,进而调用window.scheduleFrame(),再调用 PlatformDispatcher 的scheduleFrame(),代码如下:

class PlatformDispatcher {
  /// 发动 VSYNC 申请,期待下一帧调用 onBeginFrame 和 onDrawFrame 回调。void scheduleFrame() native 'PlatformConfiguration_scheduleFrame';}

PlatformDispatcher 的 scheduleFrame 办法实现其实是 Dart 调用 C/C++ native 代码,对应的也是 PlatformConfiguration_scheduleFrame,咱们能够在 engine 的 C/C++ 中搜其注册入口。

下面办法就是 Flutter 层真正发动 VSYNC 申请的中央,而后等零碎下一个 VSYNC 信号到来进行绘制操作(即来自 FlutterJni 的 nativeOnVsync 办法触发),也就是最终调用到 Dart 层的 onBeginFrame 和 onDrawFrame。

其实咱们日常中调用 Flutter Dart StatefulWidget 的 setState 办法也是调用了下面 scheduleFrame 办法,也就是说绘制的发动都来自 Widget 的变更被动调用触发,包含动画成果等也是同样情理。

收到下一帧 VSYNC 绘制信号

当下面 VSYNC 申请收回且等到下一个 VSYNC 信号到来时会通过 Java 到 C/C++ 再到 Dart Framework 层,对应到 Dart 层入口在 hooks.dart 文件(调用详见上面 Flutter Engine C/C++ 层剖析),如下:

@pragma('vm:entry-point')
// ignore: unused_element
void _beginFrame(int microseconds) {PlatformDispatcher.instance._beginFrame(microseconds);
}

@pragma('vm:entry-point')
// ignore: unused_element
void _drawFrame() {PlatformDispatcher.instance._drawFrame();
}

实质在 PlatformDispatcher 中调用对应办法,即如下:

class PlatformDispatcher {
  FrameCallback? get onBeginFrame => _onBeginFrame;
  FrameCallback? _onBeginFrame;
  Zone _onBeginFrameZone = Zone.root;
  set onBeginFrame(FrameCallback? callback) {
    _onBeginFrame = callback;
    _onBeginFrameZone = Zone.current;
  }

  VoidCallback? get onDrawFrame => _onDrawFrame;
  VoidCallback? _onDrawFrame;
  Zone _onDrawFrameZone = Zone.root;
  set onDrawFrame(VoidCallback? callback) {
    _onDrawFrame = callback;
    _onDrawFrameZone = Zone.current;
  }
}

也就是调用了 PlatformDispatcher 通过 onBeginFrame、onDrawFrame 设置的对应回调,咱们反过来推看谁设置了这个回调赋值,首先看到的调用赋值位于 SingletonFlutterWindow:

class SingletonFlutterWindow extends FlutterWindow {
  FrameCallback? get onBeginFrame => platformDispatcher.onBeginFrame;
  set onBeginFrame(FrameCallback? callback) {platformDispatcher.onBeginFrame = callback;}
}

接着看 SingletonFlutterWindow 的 onBeginFrame 属性是谁赋值的,发现赋值调用如下:

mixin SchedulerBinding on BindingBase {void ensureFrameCallbacksRegistered() {
    // 回调实现位于 SchedulerBinding 中
    window.onBeginFrame ??= _handleBeginFrame;
    window.onDrawFrame ??= _handleDrawFrame;
  }

  void scheduleFrame() {
    //......
    ensureFrameCallbacksRegistered();
    window.scheduleFrame();
    //......
  }
}

能够看到实质回到了 SchedulerBinding 的 scheduleFrame 办法,也就是说第一次 Dart 发动 VSYNC 申请前先设置了回调,当下一个零碎 VSYNC 信号到来时就调用了 onBeginFrame、onDrawFrame 的回调赋值。也就是说真正的绘制到 Dart 层入口在 SchedulerBinding 的 void handleBeginFrame(Duration? rawTimeStamp)handleDrawFrame()中,对于他们的具体内容不在本文剖析范畴,本文关注 VSYNC 动机过程。

Dart 层大抵流程如下:

Flutter Engine C/C++ 层

有了下面 Dart 层及 Java 层的剖析,咱们其实剖析 Engine 层的 C/C++ 时就大抵晓得要害入口是什么了,所以上面仍然基发动 VSYNC 申请和下一帧回调 VSYNC 信号流程进行剖析。

发动绘制 VSYNC 申请

通过 Dart 剖析得悉 VSYNC 信号的发动的实现是通过 Dart 调用了 engine C/C++ 的 PlatformConfiguration_scheduleFrame native 办法,所以咱们搜寻能够看到对应 C/C++ 只有一处注册且位于lib/ui/window/platform_configuration.cc 文件:

void PlatformConfiguration::RegisterNatives(tonic::DartLibraryNatives* natives) {
  natives->Register({
      //......
      {"PlatformConfiguration_scheduleFrame", ScheduleFrame, 1, true},
      //......
  });
}

void ScheduleFrame(Dart_NativeArguments args) {UIDartState::ThrowIfUIOperationsProhibited();
  //client()实质 PlatformConfigurationClient,也就是 RuntimeController
  UIDartState::Current()->platform_configuration()->client()->ScheduleFrame();
}

通过下面代码能够看到,Dart 调用 engine C/C++ 的 PlatformConfiguration_scheduleFrame native 办法走进了lib/ui/window/platform_configuration.cc 文件的 void ScheduleFrame(Dart_NativeArguments args) 办法,通过 platform_configuration.h 文件中能够晓得,client 是 PlatformConfigurationClient 类型,而 RuntimeController 类是他的实现,即 runtime/runtime_controller.h 中如下:

class RuntimeController : public PlatformConfigurationClient {//......}

因而咱们把眼光转向 RuntimeController 类,即 runtime/runtime_controller.h 中如下:

void RuntimeController::ScheduleFrame() {
  //client_ 类型为 RuntimeDelegate,也就是 engine instance
  client_.ScheduleFrame();}

通过 runtime/runtime_controller.h 中 client 的正文能够晓得,client_ 其实是 Engine 实例,即 shell/common/engine.h 中如下:

class Engine final : public RuntimeDelegate, PointerDataDispatcher::Delegate {//......}

对应实现 shell/common/engine.cc 如下:

void Engine::ScheduleFrame(bool regenerate_layer_tree) {animator_->RequestFrame(regenerate_layer_tree);
}

相似同上剖析模式查看对应 h 和 cpp 文件能够晓得 animator_ 位于shell/common/animator.cc,如下:

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

在引擎的 UITaskRunner 中执行 shell/common/animator.cc 文件的 AwaitVSync 办法,如下:

void Animator::AwaitVSync() {
  waiter_->AsyncWaitForVsync([self = weak_factory_.GetWeakPtr()](std::unique_ptr<FrameTimingsRecorder> frame_timings_recorder) {if (self) {if (self->CanReuseLastLayerTree()) {self->DrawLastLayerTree(std::move(frame_timings_recorder));
          } else {self->BeginFrame(std::move(frame_timings_recorder));
          }
        }
      });
}

相似同上剖析模式查看对应 h 和 cpp 文件能够晓得 waiter_ 位于 shell/common/vsync_waiter.cc,在 Android 平台的实现类是 VsyncWaiterAndroid,位于shell/platform/android/vsync_waiter_android.cc 如下:

// 父类 VsyncWaiter 的 AsyncWaitForVsync 调用子类 VsyncWaiterAndroid 的 AwaitVSync 办法
void VsyncWaiter::AsyncWaitForVsync(const Callback& callback) {
  //......
  callback_ = std::move(callback);
  //......
  // 对应 VsyncWaiterAndroid::AwaitVSync()
  AwaitVSync();}

void VsyncWaiterAndroid::AwaitVSync() {
  //......
  task_runners_.GetPlatformTaskRunner()->PostTask([java_baton]() {JNIEnv* env = fml::jni::AttachCurrentThread();
    env->CallStaticVoidMethod(
        //io/flutter/embedding/engine/FlutterJNI
        g_vsync_waiter_class->obj(),  
        //asyncWaitForVsync 办法   
        g_async_wait_for_vsync_method_,
        // 参数
        java_baton
    );
  });
}

水落石出,最初通过 Engine 的 PlatformTaskRunner 调用了 JNI 办法 asyncWaitForVsync,也就是咱们下面剖析 Android 平台 Java 层大节提到的 FlutterJNI java 类的 asyncWaitForVsync 静态方法。哈哈,Flutter 发动 VSYNC 申请的流程就这样从 Java 到 C++ 到 Dart,再从 Dart 到 C++ 到 Java 全串起来了。

收到下一帧 VSYNC 绘制信号

刚刚发动绘制 VSYNC 申请最终走进了 java 层的 Choreographer.getInstance().postFrameCallback(callback) 办法,下面剖析 Java 局部代码时也提到了,等下一帧 VSYNC 信号到来会触发 java 层 FlutterJNI 类的 nativeOnVsync 办法。通过 C/C++ 搜寻剖析可知,下面 FlutterJNI 中的 nativeOnVsync 办法调用点位于 engine 的 shell/platform/android/vsync_waiter_android.cc 中,如下:

void VsyncWaiterAndroid::OnNativeVsync(JNIEnv* env, jclass jcaller, jlong frameTimeNanos,
                                       jlong frameTargetTimeNanos, jlong java_baton) {
  //......
  ConsumePendingCallback(java_baton, frame_time, target_time);
}

void VsyncWaiterAndroid::ConsumePendingCallback( jlong java_baton,
    fml::TimePoint frame_start_time, fml::TimePoint frame_target_time) {
  //......
  shared_this->FireCallback(frame_start_time, frame_target_time);
}

void VsyncWaiter::FireCallback(fml::TimePoint frame_start_time,
                               fml::TimePoint frame_target_time,
                               bool pause_secondary_tasks) {
    //......
    PauseDartMicroTasks();
    //......
    task_runners_.GetUITaskRunner()->PostTaskForTime(
        [ui_task_queue_id, callback, flow_identifier, frame_start_time,
         frame_target_time, pause_secondary_tasks]() {
          //......
          callback(std::move(frame_timings_recorder));
          //......
          ResumeDartMicroTasks(ui_task_queue_id);
          //......
        }, frame_start_time);
  }
  //......
}

其实下面绕一圈最终就是在引擎的 UITaskRunner 中执行了下面 dart 发动 VSYNC 申请大节剖析的 callback 参数。即,这里的 callback(std::move(frame_timings_recorder)) 等价于 Animator::AwaitVSync() 办法中调用 waiter_->AsyncWaitForVsync 办法传递的参数 callback,callback 赋值代码位于 shell/common/animator.cc 文件的 AwaitVSync 办法,如下:

void Animator::AwaitVSync() {
  //AsyncWaitForVsync 函数参数就是 callback
  waiter_->AsyncWaitForVsync([self = weak_factory_.GetWeakPtr()](std::unique_ptr<FrameTimingsRecorder> frame_timings_recorder) {if (self) {if (self->CanReuseLastLayerTree()) {self->DrawLastLayerTree(std::move(frame_timings_recorder));
          } else {self->BeginFrame(std::move(frame_timings_recorder));
          }
        }
      });
}

水落石出,callback 被回调(即 VSYNC 绘制信号过去)时调用了 Animator 的 DrawLastLayerTree 或者 BeginFrame 办法,具体取决于是否须要从新生成 LayerTree 树进行绘制。

因为本文咱们次要关怀绘制动机流程,所以下面 DrawLastLayerTree 就先不剖析了,咱们看看 Animator 的 BeginFrame 办法,能够发现其调用了delegate_.OnAnimatorBeginFrame(frame_target_time, frame_number);,也就是 Shell 的 OnAnimatorBeginFrame 办法,实质就是 Engine 的 BeginFrame 办法,如下shell/common/engine.cc

void Engine::BeginFrame(fml::TimePoint frame_time, uint64_t frame_number) {TRACE_EVENT0("flutter", "Engine::BeginFrame");
  runtime_controller_->BeginFrame(frame_time, frame_number);
}

RuntimeController 的 BeginFrame 办法调用了 PlatformConfiguration 的 BeginFrame 办法,如下lib/ui/window/platform_configuration.cc

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()));
}

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

哈哈,这就响应了下面 Flutter Framework Dart 层大节收到下一帧 VSYNC 绘制信号局部被调用的入库,即下一个 VSYNC 绘制信号过去最终引擎 engine 调用了 Dart 层入口在 hooks.dart 文件的 _beginFrame_drawFrame等办法触发 dart 层进行绘制操作。

C++ 层流程大抵总结如下:

总结

到此我想你应该就能大略看懂 Flutter 官网贴的这张经典绘制流程图了:

对于上图中的每一步细节不在本文剖析范畴之内,然而对于上图从发动 Flutter VSYNC 申请到收到零碎下一个 VSYNC 绘制信号进行绘制操作的全流程咱们算是彻底搞明确了,也从肯定水平上了解了 Flutter 架构分层图的整个架构流转机制。

其实搞懂本文 VSYNC 信号从 Dart 到 C++ 到 Java,再从 Java 到 C++ 到 Dart,能够不夸大的说你曾经把握了 Flutter 架构的精华,短少的只是这条链条上的各个细节节点而已。

正文完
 0