0 前言
Flutter上手有一段时间了,但对于Engine层的逻辑一直是云里雾里,这次通过阅读源码的方式仔细梳理了一下Flutter Engine层的主体逻辑,主要包括Engine的创建、启动以及渲染流程。通过流程图和代码的方法让我们来窥探一下Engine到底在做哪些工作。(包含dart和c 代码)
首先先来熟悉一下Flutter的整体框架,Flutter的整体架构主要分为三层,Framework、Engine以及Platform,如下是官方架构图
- Framework(Dart):该层是Dart库,google实现一套用Dart语言开发的基础库,包括Widget,手势,绘图,动画等,有Material和Cupertino风格;
- Engine(C/C ):用C 编写,实现了Flutter的核心库,包括Dart虚拟机、动画和图形、文字渲染、通信通道、事件通知、插件架构等。引擎渲染采用的是2D图形渲染库Skia,虚拟机采用的是面向对象语言Dart VM,并将它们托管到平台的中间层代码(Embedder);
- Embedder(Platform Specific):嵌入层,为Engine创建和管理线程,作用是把Engine的Task Runners(任务运行器)运行在嵌入层管理的线程上
这篇文章主要是梳理Engine层也就是C 的主体调用逻辑,因为自己主要是做Android开发,所以这里会以Android为例,分别看一下Flutter Engine的创建、启动以及刷新的逻辑。
1 Flutter Engine的创建
Flutter Engine的创建在Android平台主要是伴随着FlutterActivity创建,由Java层经过JNI的调用最终调到Engine层的创建,整体流程如下图所示:
从整体的创建流程可以看出,Android平台每一个FlutterActivity都会创建对应的AndroidShellHolder、Shell、Engine、Animator、PlatformViewAndroid、RuntimeController、Window等对象。对应关系总结如下:
- 一个FlutterView对应一个Shell
- 一个Shell包含PlatformView、IOManager、Rasterizer和Engine
- Engine中创建DartIsolate
- Dart中的Isolate是一个独立的运行空间、其中包含了Platform、IO、GPU、UI四个线程,其中Platform线程是APP主线程所以是功能的
接下来我们可以通过代码,具体看一下在Engine层的创建过程中具体做了哪些工作。
首先可以看一下TaskRunners的创建代码,这部分代码会在AndroidShellHolder的构造 函数中执行。
代码语言:javascript复制if (is_background_view) {
auto single_task_runner = thread_host_.ui_thread->GetTaskRunner();
gpu_runner = single_task_runner;
ui_runner = single_task_runner;
io_runner = single_task_runner;
} else {
gpu_runner = thread_host_.raster_thread->GetTaskRunner();
ui_runner = thread_host_.ui_thread->GetTaskRunner();
io_runner = thread_host_.io_thread->GetTaskRunner();
}
flutter::TaskRunners task_runners(thread_label, // label
platform_runner, // platform
gpu_runner, // raster
ui_runner, // ui
io_runner // io
);
shell_ =
Shell::Create(task_runners, // task runners
GetDefaultWindowData(), // window data
settings_, // settings
on_create_platform_view, // platform view create callback
on_create_rasterizer // rasterizer create callback
);
这里会通过ThreadHost创建三个线程,分别是raster、ui、io线程,负责不同的工作
代码语言:javascript复制ThreadHost::ThreadHost(std::string name_prefix, uint64_t mask) {
if (mask & ThreadHost::Type::Platform) {
platform_thread = std::make_unique<fml::Thread>(name_prefix ".platform");
}
if (mask & ThreadHost::Type::UI) {
ui_thread = std::make_unique<fml::Thread>(name_prefix ".ui");
}
if (mask & ThreadHost::Type::GPU) {
raster_thread = std::make_unique<fml::Thread>(name_prefix ".raster");
}
if (mask & ThreadHost::Type::IO) {
io_thread = std::make_unique<fml::Thread>(name_prefix ".io");
}
}
然后是通过Shell::Create创建shell对象,shell的创建会通过CreateShellOnPlatformThread方法post到platform线程,从上面的流程图可以看出在方法内部会把各自线程需要初始化的工作post到对应的TaskRunner,主要包括如下
- io线程中创建ShellIOManager对象,GrContext、SkiaUnrefQueue对象
- gpu线程中创建Rasterizer对象,CompositorContext对象
- ui线程中创建Engine、Animator对象
比如,我们可以看一下UI线程的创建工作,代码如下所示:
代码语言:javascript复制std::promise<std::unique_ptr<Engine>> engine_promise;
auto engine_future = engine_promise.get_future();
fml::TaskRunner::RunNowOrPostTask(
shell->GetTaskRunners().GetUITaskRunner(), //抛到UI线程执行
fml::MakeCopyable([&engine_promise, //
shell = shell.get(), //
&dispatcher_maker, //
&window_data, //
isolate_snapshot = std::move(isolate_snapshot), //
vsync_waiter = std::move(vsync_waiter), //
&weak_io_manager_future, //
&snapshot_delegate_future, //
&unref_queue_future //
]() mutable {
TRACE_EVENT0("flutter", "ShellSetupUISubsystem");
const auto& task_runners = shell->GetTaskRunners();
// The animator is owned by the UI thread but it gets its vsync pulses
// from the platform.
auto animator = std::make_unique<Animator>(*shell, task_runners,
std::move(vsync_waiter));
engine_promise.set_value(std::make_unique<Engine>( //创建Engine
*shell, //
dispatcher_maker, //
*shell->GetDartVM(), //
std::move(isolate_snapshot), //
task_runners, //
window_data, //
shell->GetSettings(), //
std::move(animator), //
weak_io_manager_future.get(), //
unref_queue_future.get(), //
snapshot_delegate_future.get() //
));
}));
engine的创建会post到UITaskRunner,然后通过std::promise同步拿到创建后的engine
2 Flutter Engine的启动
Flutter Engine的启动在Android平台同样也是伴随着FlutterActivity的启动流程,从JAVA层一直调到Engine层启动逻辑,如下图所示
Engine的启动代码
代码语言:javascript复制Engine::RunStatus Engine::Run(RunConfiguration configuration) {
if (!configuration.IsValid()) {
FML_LOG(ERROR) << "Engine run configuration was invalid.";
return RunStatus::Failure;
}
last_entry_point_ = configuration.GetEntrypoint();
last_entry_point_library_ = configuration.GetEntrypointLibrary();
auto isolate_launch_status =
PrepareAndLaunchIsolate(std::move(configuration));
if (isolate_launch_status == Engine::RunStatus::Failure) {
FML_LOG(ERROR) << "Engine not prepare and launch isolate.";
return isolate_launch_status;
} else if (isolate_launch_status ==
Engine::RunStatus::FailureAlreadyRunning) {
return isolate_launch_status;
}
std::shared_ptr<DartIsolate> isolate =
runtime_controller_->GetRootIsolate().lock();
bool isolate_running =
isolate && isolate->GetPhase() == DartIsolate::Phase::Running;
if (isolate_running) {
tonic::DartState::Scope scope(isolate.get());
if (settings_.root_isolate_create_callback) {
settings_.root_isolate_create_callback();
}
if (settings_.root_isolate_shutdown_callback) {
isolate->AddIsolateShutdownCallback(
settings_.root_isolate_shutdown_callback);
}
std::string service_id = isolate->GetServiceId();
fml::RefPtr<PlatformMessage> service_id_message =
fml::MakeRefCounted<flutter::PlatformMessage>(
kIsolateChannel,
std::vector<uint8_t>(service_id.begin(), service_id.end()),
nullptr);
HandlePlatformMessage(service_id_message);
}
return isolate_running ? Engine::RunStatus::Success
: Engine::RunStatus::Failure;
}
Engine的启动经过PrepareAndLaunchIsolate调用最终执行到DartIsolate::Run,代码如下
代码语言:javascript复制[[nodiscard]] bool DartIsolate::Run(const std::string& entrypoint_name,
const std::vector<std::string>& args,
const fml::closure& on_run) {
TRACE_EVENT0("flutter", "DartIsolate::Run");
if (phase_ != Phase::Ready) {
return false;
}
tonic::DartState::Scope scope(this);
auto user_entrypoint_function =
Dart_GetField(Dart_RootLibrary(), tonic::ToDart(entrypoint_name.c_str()));
auto entrypoint_args = tonic::ToDart(args);
if (!InvokeMainEntrypoint(user_entrypoint_function, entrypoint_args)) {
return false;
}
phase_ = Phase::Running;
if (on_run) {
on_run();
}
return true;
}
经过Dart虚拟机最终会调用的Dart层的_runMainZoned()方法
代码语言:javascript复制[[nodiscard]] static bool InvokeMainEntrypoint(
Dart_Handle user_entrypoint_function,
Dart_Handle args) {
if (tonic::LogIfError(user_entrypoint_function)) {
FML_LOG(ERROR) << "Could not resolve main entrypoint function.";
return false;
}
Dart_Handle start_main_isolate_function =
tonic::DartInvokeField(Dart_LookupLibrary(tonic::ToDart("dart:isolate")),
"_getStartMainIsolateFunction", {});
if (tonic::LogIfError(start_main_isolate_function)) {
FML_LOG(ERROR) << "Could not resolve main entrypoint trampoline.";
return false;
}
if (tonic::LogIfError(tonic::DartInvokeField(
Dart_LookupLibrary(tonic::ToDart("dart:ui")), "_runMainZoned",
{start_main_isolate_function, user_entrypoint_function, args}))) {
FML_LOG(ERROR) << "Could not invoke the main entrypoint.";
return false;
}
return true;
}
通过DartInvokeField
执行到Dart
层的main()
方法入口,这样整个Dart
代码就跑起来了。
3 Flutter Engine的刷新
做过Flutter开发都知道,UI界面的开发主要是通过组合各种不同的widget来搭建widget树,最终渲染出相要的应用界面, 常见的Widget子类为StatelessWidget(无状态)和StatefulWidget(有状态);
- StatelessWidget:内部没有保存状态,界面创建后不会发生改变;
- StatefulWidget:内部有保存状态,当状态发生改变,调用setState()方法会触发StatefulWidget的UI发生更新,对于自定义继承自StatefulWidget的子类,必须要重写createState()方法。
widget节点的更新是通过SetState触发,我们来看一下此方法在Engine层主要做了哪些工作
首先SetState会触发注册vsyn的调用逻辑,如下图所示
经过层层调用,最终会注册Vsync回调。 等待下一次vsync信号的到来,在通过代码看一下vsync信号到来时的回调。
代码语言:javascript复制void Animator::AwaitVSync() {
waiter_->AsyncWaitForVsync(
[self = weak_factory_.GetWeakPtr()](fml::TimePoint frame_start_time,
fml::TimePoint frame_target_time) {
if (self) {
if (self->CanReuseLastLayerTree()) {
self->DrawLastLayerTree();
} else {
self->BeginFrame(frame_start_time, frame_target_time);
}
}
});
delegate_.OnAnimatorNotifyIdle(dart_frame_deadline_);
}
如果可以直接复用之前的LayerTree则,直接用上一次的layertree进行光栅化,这里我们主要看下一下else部分的逻辑,也就是self->BeginFrame 之后的流程,可以先来看一下流程图。
Engine层调用会通过invoke回到Dart层的调用,WidgetsBinding::drawFrame里面主要是dart层的一系列刷新、合成LayerTree的逻辑,最后会BuildScene生成Dart层的Scene结构,其代码步骤如下:
代码语言:javascript复制// dart代码
@override
void drawFrame() {
assert(inTest);
try {
debugBuildingDirtyElements = true;
buildOwner.buildScope(renderViewElement);
if (_phase != EnginePhase.build) {
assert(renderView != null);
pipelineOwner.flushLayout();
if (_phase != EnginePhase.layout) {
pipelineOwner.flushCompositingBits();
if (_phase != EnginePhase.compositingBits) {
pipelineOwner.flushPaint();
if (_phase != EnginePhase.paint && sendFramesToEngine) {
_firstFrameSent = true;
renderView.compositeFrame(); // this sends the bits to the GPU
if (_phase != EnginePhase.composite) {
pipelineOwner.flushSemantics();
assert(_phase == EnginePhase.flushSemantics ||
_phase == EnginePhase.sendSemanticsUpdate);
}
}
}
}
}
buildOwner.finalizeTree();
} finally {
debugBuildingDirtyElements = false;
}
}
- buildScope: 对于dirty的元素会执行build构造,没有dirty元素则不会执行
- flushLayout: 计算渲染对象的大小和位置,这个过程可能会嵌套再调用build操作;
- flushCompositingBits: 更新具有脏合成位的任何渲染对象;
- flushPaint: 将绘制命令记录到Layer;
- compositeFrame: 将Compositing bits发送给GPU;
- flushSemantics: 编译渲染对象的语义,并将语义发送给操作系统
整个流程主要是compositeFrame会触发到Engine的Frame刷新逻辑,compositeFrame会把dart层构建好的Scene传到Engine层,调用window的native方法,代码如下:
代码语言:javascript复制// dart 代码
void compositeFrame() {
Timeline.startSync('Compositing', arguments: timelineArgumentsIndicatingLandmarkEvent);
try {
final ui.SceneBuilder builder = ui.SceneBuilder();
final ui.Scene scene = layer!.buildScene(builder);
if (automaticSystemUiAdjustment)
_updateSystemChrome();
_window.render(scene);
scene.dispose();
assert(() {
if (debugRepaintRainbowEnabled || debugRepaintTextRainbowEnabled)
debugCurrentRepaintColor = debugCurrentRepaintColor.withHue((debugCurrentRepaintColor.hue 2.0) % 360.0);
return true;
}());
} finally {
Timeline.finishSync();
}
}
到了Engine层首先会把Scene的数据结构转成Engine对应的Scene的数据结构
代码语言:javascript复制void Render(Dart_NativeArguments args) {
UIDartState::ThrowIfUIOperationsProhibited();
Dart_Handle exception = nullptr;
Scene* scene =
tonic::DartConverter<Scene*>::FromArguments(args, 1, exception);
if (exception) {
Dart_ThrowException(exception);
return;
}
UIDartState::Current()->window()->client()->Render(scene);
}
接着就会调用Engine层的渲染过程,我们可以看一下Engine层render的具体流程,如下图所示
最后会调到Shell方法OnAnimatorDraw
代码语言:javascript复制// |Animator::Delegate|
void Shell::OnAnimatorDraw(fml::RefPtr<Pipeline<flutter::LayerTree>> pipeline,
fml::TimePoint frame_target_time) {
FML_DCHECK(is_setup_);
// record the target time for use by rasterizer.
{
std::scoped_lock time_recorder_lock(time_recorder_mutex_);
if (!latest_frame_target_time_) {
latest_frame_target_time_ = frame_target_time;
} else if (latest_frame_target_time_ < frame_target_time) {
latest_frame_target_time_ = frame_target_time;
}
}
task_runners_.GetRasterTaskRunner()->PostTask(
[&waiting_for_first_frame = waiting_for_first_frame_,
&waiting_for_first_frame_condition = waiting_for_first_frame_condition_,
rasterizer = rasterizer_->GetWeakPtr(),
pipeline = std::move(pipeline)]() {
if (rasterizer) {
rasterizer->Draw(pipeline);
if (waiting_for_first_frame.load()) {
waiting_for_first_frame.store(false);
waiting_for_first_frame_condition.notify_all();
}
}
});
}
这里就到了最后的光栅化阶段,把构建的pipeline post到RasterTaskRunner执行rasterizer的光栅化,完成上屏操作。