30分钟彻底了解Flutter整个渲染流程(超详细)
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; @override void 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(); } @override void 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()做了以下几件事情:
- 创建个WidgetsFlutterBinding实例
- 初始化, SchedulerBinding, RendererBinding, WidgetsBinding 等所有单例
- WidgetsBinding.instance=SchedulerBinding.instance=RendererBinding.instance=WidgetsFlutterBinding()
- 然后执行SchedulerBinding, RendererBinding, WidgetsBinding 所有类的initInstances,initServiceExtensions方法
- 完成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做如下几件事情:
- 将所有树绑定关联起来
- 设置好Vsync信号下一帧回来执行的回调(后面验证 window是在哪里被执行的)
window.onBeginFrame ??= _handleBeginFrame;
window.onDrawFrame ??= _handleDrawFrame;
- 请求获取第一帧的绘制
- 强制执行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 { //... @override final 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个方法被回调就会进行事件任务的执行,以及布局绘制的工作。
终于,到这里所有流程都串通了.
我们重新梳理下所有流程:
- 先初始化SchedulerBinding,RendererBinding,WidgetsBinding单例,确保不为空
- RendererBinding绑定persistentFrameCallback每次收到Vsync就进行布局和绘制工作
- 将SchedulerBinding的_handleBeginFrame,_handleDrawFrame通过window.onBeginFrame, onDrawFrame方法被绑定到platformDispatcher
- 初始化三棵树的绑定
- 申请第一个Vsync绘制第一帧
- 执行native方法从而让c++代码向安卓发出请求Vsync请求并绑定回调
- 安卓获取到Vsync后调用JNI传递Vsync给c++, 然后c++调用SchedulerBinding的_handleBeginFrame,_handleDrawFrame从而完成一帧的工作
好了,所以流程已经梳理完毕,是不是很赞?如果这篇文章对你有帮助,请关注🙏,点赞👍,收藏😋三连哦