Flutter是Google開發的一套全新的跨平臺、開源UI框架,支援iOS、Android系統開發,並且是未來新作業系統Fuchsia的預設開發套件。自從2017年5月釋出第一個版本以來,目前Flutter已經發布了近60個版本,並且在2018年5月釋出了第一個“Ready for Production Apps”的Beta 3版本,6月20日釋出了第一個“Release Preview”版本。Flutter的目標是使同一套程式碼同時執行在Android和iOS系統上,並且擁有媲美原生應用的效能,Flutter甚至提供了兩套控制元件來適配Android和iOS(滾動效果、字型和控制元件圖示等等)為了讓App在細節處看起來更像原生應用。
FlutterEngine是一個跨平臺的UI框架,可以在不同的平臺上執行同一套程式碼,整個渲染過程如下圖所示,下面接著分析整個渲染過程
RuntimeController&WindowClient&Window關係
RuntimeController 作為FlutterEngine引擎初始化Dart相關的環境的分析在開一篇來說明,這篇文章還是在說明整個FlutterEngine和FlutterUI和平臺之間的一個互動框架,整體的框架理解了之後,我們在來對具體的每一個知識點進行分析,同時修改原始碼邏輯。詳細的資訊已經在FlutterEngine啟動過程中進行分析。
RuntimeController
在前面的文字中,介紹了FlutterEngine初始化過程,FlutterEngine的初始化時,建立了RuntimeController
做為Engine的代理物件,負責處理FlutterUI層和底層通訊的介面,作為FlutterEngine的通訊和排程中心
engine/src/flutter/runtime/runtime_controller.cc
Engine::Engine(Delegate& delegate,
blink::DartVM& vm,
fml::RefPtr<blink::DartSnapshot> isolate_snapshot,
fml::RefPtr<blink::DartSnapshot> shared_snapshot,
blink::TaskRunners task_runners,
blink::Settings settings,
std::unique_ptr<Animator> animator,
fml::WeakPtr<blink::SnapshotDelegate> snapshot_delegate,
fml::WeakPtr<blink::IOManager> io_manager)
: delegate_(delegate),
settings_(std::move(settings)),
animator_(std::move(animator)),
activity_running_(false),
have_surface_(false),
weak_factory_(this) {
// Runtime controller is initialized here because it takes a reference to this
// object as its delegate. The delegate may be called in the constructor and
// we want to be fully initilazed by that point.
runtime_controller_ = std::make_unique<blink::RuntimeController>(
*this, // runtime delegate
&vm, // VM
std::move(isolate_snapshot), // isolate snapshot
std::move(shared_snapshot), // shared snapshot
std::move(task_runners), // task runners
std::move(snapshot_delegate), // snapshot delegate
std::move(io_manager), // io manager
settings_.advisory_script_uri, // advisory script uri
settings_.advisory_script_entrypoint, // advisory script entrypoint
settings_.idle_notification_callback // idle notification callback
);
}
複製程式碼
RuntimeController初始化時,作為flutter/runtime/runtime_delegate.cc
代理物件,同時持有DartVM
物件,IOManager
管理上下文物件,最終建立Dart執行的DartIsolate
物件,isolate是Dart對actor併發模式的實現。執行中的Dart程式由一個或多個actor組成,這些actor也就是Dart概念裡面的isolate。isolate是有自己的記憶體和單執行緒控制的執行實體。isolate本身的意思是“隔離”,因為isolate之間的記憶體在邏輯上是隔離的。isolate中的程式碼是按順序執行的,任何Dart程式的併發都是執行多個isolate的結果。因為Dart沒有共享記憶體的併發,沒有競爭的可能性所以不需要鎖,也就不用擔心死鎖的問題。
flutterisolate.png
isolate之間的通訊
由於isolate之間沒有共享記憶體,所以他們之間的通訊唯一方式只能是通過Port進行,而且Dart中的訊息傳遞總是非同步的。
isolate與普通執行緒的區別
我們可以看到isolate神似Thread,但實際上兩者有本質的區別。作業系統內內的執行緒之間是可以有共享記憶體的而isolate沒有,這是最為關鍵的區別。
isolate實現簡述 我們可以閱讀Dart原始碼裡面的isolate.cc檔案看看isolate的具體實現。 我們可以看到在isolate建立的時候有以下幾個主要步驟:
初始化isolate資料結構 初始化堆記憶體(Heap) 進入新建立的isolate,使用跟isolate一對一的執行緒執行isolate 配置Port 配置訊息處理機制(Message Handler) 配置Debugger,如果有必要的話 將isolate註冊到全域性監控器(Monitor)
Flutter Engine Runners與Dart Isolate
有朋友看到這裡可能會問既然Flutter Engine有自己的Runner,那為何還要Dart的Isolate呢,他們之間又是什麼關係呢?
那我們還要從Runner具體的實現說起,Runner是一個抽象概念,我們可以往Runner裡面提交任務,任務被Runner放到它所在的執行緒去執行,這跟iOS GCD的執行佇列很像。我們檢視iOS Runner的實現實際上裡面是一個loop,這個loop就是CFRunloop,在iOS平臺上Runner具體實現就是CFRunloop。被提交的任務被放到CFRunloop去執行。
Dart的Isolate是Dart虛擬機器自己管理的,Flutter Engine無法直接訪問。Root Isolate通過Dart的C++呼叫能力把UI渲染相關的任務提交到UI Runner執行這樣就可以跟Flutter Engine相關模組進行互動,Flutter UI相關的任務也被提交到UI Runner也可以相應的給Isolate一些事件通知,UI Runner同時也處理來自App方面Native Plugin的任務。
所以簡單來說Dart isolate跟Flutter Runner是相互獨立的,他們通過任務排程機制相互協作。
RuntimeController初始化
1.建立DartIsolate
物件
2.獲取Window物件
3.載入dart:ui程式碼到虛擬機器中DidCreateIsolate
4.初始化FlutterUI層的引數FlushRuntimeStateToIsolate
RuntimeController::RuntimeController(
RuntimeDelegate& p_client,
DartVM* p_vm,
fml::RefPtr<DartSnapshot> p_isolate_snapshot,
fml::RefPtr<DartSnapshot> p_shared_snapshot,
TaskRunners p_task_runners,
fml::WeakPtr<SnapshotDelegate> p_snapshot_delegate,
fml::WeakPtr<IOManager> p_io_manager,
std::string p_advisory_script_uri,
std::string p_advisory_script_entrypoint,
std::function<void(int64_t)> idle_notification_callback,
WindowData p_window_data)
: client_(p_client),
vm_(p_vm),
isolate_snapshot_(std::move(p_isolate_snapshot)),
shared_snapshot_(std::move(p_shared_snapshot)),
task_runners_(p_task_runners),
snapshot_delegate_(p_snapshot_delegate),
io_manager_(p_io_manager),
advisory_script_uri_(p_advisory_script_uri),
advisory_script_entrypoint_(p_advisory_script_entrypoint),
idle_notification_callback_(idle_notification_callback),
window_data_(std::move(p_window_data)),
root_isolate_(
DartIsolate::CreateRootIsolate(vm_,
isolate_snapshot_,
shared_snapshot_,
task_runners_,
std::make_unique<Window>(this),
snapshot_delegate_,
io_manager_,
p_advisory_script_uri,
p_advisory_script_entrypoint)) {
std::shared_ptr<DartIsolate> root_isolate = root_isolate_.lock();
root_isolate->SetReturnCodeCallback([this](uint32_t code) {
root_isolate_return_code_ = {true, code};
});
if (auto* window = GetWindowIfAvailable()) {
tonic::DartState::Scope scope(root_isolate);
///ISOlate建立完成
window->DidCreateIsolate();
if (!FlushRuntimeStateToIsolate()) {
FML_DLOG(ERROR) << "Could not setup intial isolate state.";
}
} else {
FML_DCHECK(false) << "RuntimeController created without window binding.";
}
FML_DCHECK(Dart_CurrentIsolate() == nullptr);
}
複製程式碼
這裡需要一篇檔案繼續展開來講解,我們現在的目的是搞清楚整個邏輯呼叫過程的主幹,在來了解整個DartIsolate
建立過程
參考:
engine/src/flutter/lib/ui/ui_dart_state.cc engine/src/flutter/runtime/dart_isolate.cc
WindowClient
RuntimeController 實現了WindowClient
物件engine/src/flutter/lib/ui/window/window.h
,
class WindowClient {
public:
virtual std::string DefaultRouteName() = 0;
virtual void ScheduleFrame() = 0;
virtual void Render(Scene* scene) = 0;
virtual void UpdateSemantics(SemanticsUpdate* update) = 0;
virtual void HandlePlatformMessage(fml::RefPtr<PlatformMessage> message) = 0;
virtual FontCollection& GetFontCollection() = 0;
virtual void UpdateIsolateDescription(const std::string isolate_name,
int64_t isolate_port) = 0;
protected:
virtual ~WindowClient();
};
複製程式碼
Window::RegisterNatives
flutterUI層的ui.window.dart和FlutterEngine層的Window.cc建立管理關係,通過RuntimeController
和
void Window::RegisterNatives(tonic::DartLibraryNatives* natives) {
natives->Register({
{"Window_defaultRouteName", DefaultRouteName, 1, true},
{"Window_scheduleFrame", ScheduleFrame, 1, true},
{"Window_sendPlatformMessage", _SendPlatformMessage, 4, true},
{"Window_respondToPlatformMessage", _RespondToPlatformMessage, 3, true},
{"Window_render", Render, 2, true},
{"Window_updateSemantics", UpdateSemantics, 2, true},
{"Window_setIsolateDebugName", SetIsolateDebugName, 2, true},
{"Window_reportUnhandledException", ReportUnhandledException, 2, true},
});
}
複製程式碼
小結
上面的介紹主要是理清楚FlutterEngine初始化在建立FlutterEngine和FlutterUI層的一個呼叫關係,在FlutterUI層回撥FlutterEngine時,會呼叫window.dart的本地方法,從而,呼叫FlutterEngine的Window.cc類,RuntimeController作為Engine的代理物件,實現了WindowClient類,Window物件中次有WindowClient物件的引用,也就是持有RuntimeController引用物件,從而和Engine能夠進行相互呼叫,言歸正傳,接下分析一下FlutterUI層在構建好一幀之後,是怎麼新增到FlutterEngine中進行顯示的。
Android端是怎麼把SurfaceView註冊到FlutterEngine中的
Android端在初始化是自定義SurfaceView作為彙總Flutter的View,在載入libflutter.so庫是新增到FlutterEngine中,具體呼叫步驟參考下面的程式碼,前面的檔案已經分析過來FlutterEngine啟動過程中SurfaceView註冊到FlutterEngine引擎的過程
engine/src/flutter/shell/platform/android/library_loader.cc
1.JNI_OnLoad註冊本地方法
2.PlatformViewAndroid::Register(env);註冊本地方法
3.SurfaceCreated通過本地方法呼叫和AndroidNative層的View進行關聯
4.PlatformViewAndroid中中進行引用
// This is called by the VM when the shared library is first loaded.
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
// Initialize the Java VM.
fml::jni::InitJavaVM(vm);
JNIEnv* env = fml::jni::AttachCurrentThread();
bool result = false;
// Register FlutterMain.
result = shell::FlutterMain::Register(env);
FML_CHECK(result);
// Register PlatformView
result = shell::PlatformViewAndroid::Register(env);
FML_CHECK(result);
// Register VSyncWaiter.
result = shell::VsyncWaiterAndroid::Register(env);
FML_CHECK(result);
return JNI_VERSION_1_4;
}
複製程式碼
AndroidNativeWindow
static void SurfaceCreated(JNIEnv* env,
jobject jcaller,
jlong shell_holder,
jobject jsurface) {
// Note: This frame ensures that any local references used by
// ANativeWindow_fromSurface are released immediately. This is needed as a
// workaround for https://code.google.com/p/android/issues/detail?id=68174
fml::jni::ScopedJavaLocalFrame scoped_local_reference_frame(env);
auto window = fml::MakeRefCounted<AndroidNativeWindow>(
ANativeWindow_fromSurface(env, jsurface));
ANDROID_SHELL_HOLDER->GetPlatformView()->NotifyCreated(std::move(window));
}
複製程式碼
PlatformViewAndroid
void PlatformViewAndroid::NotifyCreated(
fml::RefPtr<AndroidNativeWindow> native_window) {
if (android_surface_) {
InstallFirstFrameCallback();
android_surface_->SetNativeWindow(native_window);
}
PlatformView::NotifyCreated();
}
複製程式碼
AndroidSurfaceSoftware
bool AndroidSurfaceSoftware::SetNativeWindow(
fml::RefPtr<AndroidNativeWindow> window) {
native_window_ = std::move(window);
if (!(native_window_ && native_window_->IsValid()))
return false;
int32_t window_format = ANativeWindow_getFormat(native_window_->handle());
if (window_format < 0)
return false;
if (!GetSkColorType(window_format, &target_color_type_, &target_alpha_type_))
return false;
return true;
}
複製程式碼
呼叫FlutterEngine進行初始化
Window:render 方法
在上一篇中分析了FlutterUI的初始化過程,FlutterUI初始化完成之後,就把構建好的Scene傳遞到FlutterEngine層進行渲染,呼叫ui.window的本地方法,void render(Scene scene) native 'Window_render';
上一個部分在FlutterEngine中註冊的本地方法,
/// Uploads the composited layer tree to the engine.
///
/// Actually causes the output of the rendering pipeline to appear on screen.
void compositeFrame() {
Timeline.startSync('Compositing', arguments: timelineWhitelistArguments);
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();
}
}
複製程式碼
Window:render
通過Window物件對呼叫Engine的方法
engine/src/flutter/lib/ui/window/window.cc中把資料傳遞個sky引擎進行渲染
void Render(Dart_NativeArguments args) {
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);
}
複製程式碼
RuntimeController::Render
整個呼叫邏輯都很接單
在Engine代理物件RuntimeController::Render方法中,獲取Scene的Layer資料,並且呼叫Engine:render方法,進入FlutterEngine核心處理邏輯中
void RuntimeController::Render(Scene* scene) {
client_.Render(scene->takeLayerTree());
}
複製程式碼
Engine::Render
void Engine::Render(std::unique_ptr<flow::LayerTree> layer_tree) {
if (!layer_tree)
return;
SkISize frame_size = SkISize::Make(viewport_metrics_.physical_width,
viewport_metrics_.physical_height);
if (frame_size.isEmpty())
return;
layer_tree->set_frame_size(frame_size);
animator_->Render(std::move(layer_tree));
}
複製程式碼
Animator::Render
void Animator::Render(std::unique_ptr<flow::LayerTree> layer_tree) {
if (dimension_change_pending_ &&
layer_tree->frame_size() != last_layer_tree_size_) {
dimension_change_pending_ = false;
}
last_layer_tree_size_ = layer_tree->frame_size();
if (layer_tree) {
// Note the frame time for instrumentation.
layer_tree->set_construction_time(fml::TimePoint::Now() -
last_begin_frame_time_);
}
// Commit the pending continuation.
producer_continuation_.Complete(std::move(layer_tree));
delegate_.OnAnimatorDraw(layer_tree_pipeline_);
}
複製程式碼
Shell::OnAnimatorDraw
呼叫平臺端的持久化物件進行柵格化rasterizer
// |shell::Animator::Delegate|
void Shell::OnAnimatorDraw(
fml::RefPtr<flutter::Pipeline<flow::LayerTree>> pipeline) {
FML_DCHECK(is_setup_);
task_runners_.GetGPUTaskRunner()->PostTask(
[rasterizer = rasterizer_->GetWeakPtr(),
pipeline = std::move(pipeline)]() {
if (rasterizer) {
rasterizer->Draw(pipeline);
}
});
}
複製程式碼
Rasterizer::Draw
void Rasterizer::Draw(
fml::RefPtr<flutter::Pipeline<flow::LayerTree>> pipeline) {
TRACE_EVENT0("flutter", "GPURasterizer::Draw");
flutter::Pipeline<flow::LayerTree>::Consumer consumer =
std::bind(&Rasterizer::DoDraw, this, std::placeholders::_1);
// Consume as many pipeline items as possible. But yield the event loop
// between successive tries.
switch (pipeline->Consume(consumer)) {
case flutter::PipelineConsumeResult::MoreAvailable: {
task_runners_.GetGPUTaskRunner()->PostTask(
[weak_this = weak_factory_.GetWeakPtr(), pipeline]() {
if (weak_this) {
weak_this->Draw(pipeline);
}
});
break;
}
default:
break;
}
}
複製程式碼
上面的過程中已經把FltuterUI層生成的資料新增到了Skia中,接下來時要把Skia已經渲染好的資料,新增到SurfaceView上進行渲染
ui.Window.dart
void scheduleFrame() native 'Window_scheduleFrame';
呼叫通知FlutterEngine已經準備好了一幀,可以渲染到Android提供的SurfaceView上
engine/src/flutter/lib/ui/window/window.cc
作為FlutterEngine的介面呼叫,整個呼叫過程很簡單,中間省掉了部分呼叫過程,可以從window.cc開始追蹤呼叫過程
Animator::RequestFrame
void Animator::RequestFrame(bool regenerate_layer_tree) {
if (regenerate_layer_tree) {
regenerate_layer_tree_ = true;
}
if (paused_ && !dimension_change_pending_) {
return;
}
if (!pending_frame_semaphore_.TryWait()) {
// Multiple calls to Animator::RequestFrame will still result in a
// single request to the VsyncWaiter.
return;
}
// The AwaitVSync is going to call us back at the next VSync. However, we want
// to be reasonably certain that the UI thread is not in the middle of a
// particularly expensive callout. We post the AwaitVSync to run right after
// an idle. This does NOT provide a guarantee that the UI thread has not
// started an expensive operation right after posting this message however.
// To support that, we need edge triggered wakes on VSync.
task_runners_.GetUITaskRunner()->PostTask([self = weak_factory_.GetWeakPtr(),
frame_number = frame_number_]() {
if (!self.get()) {
return;
}
TRACE_EVENT_ASYNC_BEGIN0("flutter", "Frame Request Pending", frame_number);
self->AwaitVSync();
});
frame_scheduled_ = true;
}
複製程式碼
Rasterizer::DrawToSurface
在這裡正在的合成一幀,張上一個步驟中已經把相關的資料傳遞到AndroidNative層
bool Rasterizer::DrawToSurface(flow::LayerTree& layer_tree) {
FML_DCHECK(surface_);
auto frame = surface_->AcquireFrame(layer_tree.frame_size());
if (frame == nullptr) {
return false;
}
// There is no way for the compositor to know how long the layer tree
// construction took. Fortunately, the layer tree does. Grab that time
// for instrumentation.
compositor_context_->engine_time().SetLapTime(layer_tree.construction_time());
auto* canvas = frame->SkiaCanvas();
auto* external_view_embedder = surface_->GetExternalViewEmbedder();
if (external_view_embedder != nullptr) {
external_view_embedder->BeginFrame(layer_tree.frame_size());
}
auto compositor_frame = compositor_context_->AcquireFrame(
surface_->GetContext(), canvas, external_view_embedder,
surface_->GetRootTransformation(), true);
if (compositor_frame && compositor_frame->Raster(layer_tree, false)) {
frame->Submit();
if (external_view_embedder != nullptr) {
external_view_embedder->SubmitFrame(surface_->GetContext());
}
FireNextFrameCallbackIfPresent();
if (surface_->GetContext())
surface_->GetContext()->performDeferredCleanup(kSkiaCleanupExpiration);
return true;
}
return false;
}
複製程式碼
小結
從上面的呼叫過程來看這個呼叫過程並不複雜
1.FlutterUI層把渲染好的資料通過Window.cc的Render方法呼叫到Shell
上繼續持久化
2.將這邊好的資料通過Vsync通過系統繪製到螢幕上
整個FlutterEngine對FlutterUI彙總到螢幕上的操作控制還是比較少,整個流程呼叫也非常單一