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();
}
void initInstances() {
//...
//做debug模式的初始化活
//...
}
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;
void initInstances() {
super.initInstances();
_instance = this;
//..
}
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;
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();
}
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;
void initInstances() {
super.initInstances();
_instance = this;
//...
_buildOwner = BuildOwner();
buildOwner!.onBuildScheduled = _handleBuildScheduled;
//..
}
void initServiceExtensions() {
//...
//做debug模式的初始化活
//...
}
BuildOwner是整棵Element的树的管理类。每个Element都会有这个BuildOwner的唯一实例。BuildOwner在WidgetsBinding绑定了onBuildScheduled方法,也就是_handleBuildScheduled, 这个方法会调用ensureVisualUpdate,然后调用SchedulerBinding的 scheduleFrame方法,从而可以申请Vsync信号,从而获取下一帧的绘制。
_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
void setState(VoidCallback fn) {
_element!.markNeedsBuild();
}
Element
BuildOwner? _owner;
void markNeedsBuild() {
//...
_dirty = true;
owner!.scheduleBuildFor(this);
//..
}
BuildOwner
void scheduleBuildFor(Element element) {
//..
_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
void scheduleAttachRootWidget(Widget rootWidget) {
Timer.run(() {
attachRootWidget(rootWidget);
});
}
//这个方法将Widget,Render树都绑定在Element树上
void attachRootWidget(Widget rootWidget) {
final bool isBootstrapFrame = renderViewElement == null;
_readyToProduceFrames = true;
_renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
container: renderView,
debugShortDescription: '[root]',
child: rootWidget,
).attachToRenderTree(buildOwner!, renderViewElement as RenderObjectToWidgetElement<RenderBox>?);
//如果是第一次,这里会执行
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<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;
}
}
//执行addPersistentFrameCallback和addPostFrameCallback中注册的回调
//
void handleDrawFrame() {
//...
_schedulerPhase = SchedulerPhase.persistentCallbacks;
for (final FrameCallback callback in _persistentCallbacks)
_invokeFrameCallback(callback, _currentFrameTimeStamp!);
// POST-FRAME CALLBACKS
_schedulerPhase = SchedulerPhase.postFrameCallbacks;
final List<FrameCallback> localPostFrameCallbacks =
List<FrameCallback>.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 {
//...
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<VsyncWaiter> waiter_;
}
void Animator::AwaitVSync() {
waiter_->AsyncWaitForVsync(
[self = weak_factory_.GetWeakPtr()](
std::unique_ptr<FrameTimingsRecorder> 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 < 0) {
delay = 0;
}
flutterJNI.onVsync(delay, refreshPeriodNanos, cookie);
}
});
}
};
}
,好家伙,原来他是用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<FrameTimingsRecorder> 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++调用的代码其中有
('vm:entry-point')
void _drawFrame() {
PlatformDispatcher.instance._drawFrame();
}
('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从而完成一帧的工作
好了,所以流程已经梳理完毕,是不是很赞?如果这篇文章对你有帮助,请关注🙏,点赞👍,收藏😋三连哦