環境: flutter sdk v1.7.8+hotfix.3@stable
對應 flutter engine: 54ad777fd29b031b87c7a68a6637fb48c0932862
在建立異步線程與消息循環之后,自然就是運行應用腳本,也就是dart文件。這一部分感覺很龐大而且千頭萬緒:對dart不同模式的編譯,不同參數的配置,從代碼看還有熱加載(hot reload)的機制,從里到外都是一團亂麻;有這種感覺只是因為不熟悉,剛剛接觸陌生環境產生的畏懼,只要熟悉啥都不是事。所以先不貿然進入熱加載之類的細節,以目前了解的通信與異步為基礎,漸次深入對象關聯關系為上。
在FlutterActivityDelegate.onCreate的最后容易發現一個比較重要的調用runBundle ,深入的調用序列如下:
FlutterActivity.onCreate
FlutterActivityDelegate.onCreate
FlutterActivityDelegate.runBundle
FlutterView.runFromBundle
FlutterView.preRun
FlutterNativeView.runFromBundle
FlutterNativeView.runFromBundleInternal
FlutterJNI.runBundleAndSnapshotFromLibrary
FlutterJNI.nativeRunBundleAndSnapshotFromLibrary
FlutterView.postRun
與C++層的調用序列分開:
::RunBundleAndSnapshotFromLibrary
AndroidShellHolder::Launch
...[async:ui_thread]Engine::Run
Engine::PrepareAndLaunchIsolate
RuntimeController::GetRootIsolate
IsolateConfiguration::PrepareIsolate
IsolateConfiguration::DoPrepareIsolate => AppSnapshotIsolateConfiguration::DoPrepareIsolate
DartIsolate::PrepareForRunningFromPrecompiledCode
DartIsolate::Run
DartIsolate::InvokeMainEntrypoint
這里已經有點暈了,各種名稱堆砌在一起:DartIsolate, Dart_Isolate, RootDartIsolate;
RunConfiguration, IsolateConfiguration
AppSnapshotIsolateConfiguration; 撇開這些名稱至少我們知道:
AndroidShellHolder異步調用了Engine的Run方法Engine的Run跑在flutter的ui線程中Engine獲取成員RuntimeController的一個叫RootIsolate的對象並最終調用了其DartIsolate::Run方法DartIsolate進入到了主入口方法,在這里就是lib/main.dart中的main()方法(runFromBundle(bundlePath, defaultPath, "main", false);FlutterView.java:611)。
調用封裝
顯然最終調的是dartSDK提供的各種方法,雖然我們大概知道flutter的Engine不會具體做dart代碼的解釋與執行,但比較棘手的是我們很難分清Engine與DartSDK的界限;DartSDK的接口方法散落在各處,他們的先后調用關系,對象依賴關系,內部狀態的變化與檢查,對於初學者都增加理解上的難度。所以最好是針對DartSDK再有一層封裝或者抽象,不僅初始化與運行調用序列清晰,讓sdk可替換(如果以后有其它的dart實現呢?),也讓引擎真正成為引擎。
所謂引擎
所以這里也可以對引擎的含義做一個梳理:引擎自然是可插撥的一種形態,只要與引擎提供的接口一致可以更換別的實現如同燈泡座與燈泡的關系,在這里顯然無法更換DartSDK, 所以Flutter的引擎是針對平台的引擎,我們可以將應用移植到各種平台或者操作系統。
文檔理解
這時候死看代碼難有進展,我們最好先了解DartSDK本來有什么。但發現竟然很難找到一份針對DartSDK的使用教程與文檔(不是Dart語言使用文檔,是開發集成Dart虛擬機的C接口文檔),它的初始化,運行,集成像一個巨大的黑盒。因為最終運行的還是Isolate的Run方法,核心還是理解Dart的Isolate。
一些資料
Engine-architecture里的UI Task Runner提到:
(root Dart isolate)runs the application's main Dart code. Bindings are set up on this isolate by the engine to schedule and submit frames.
Terminating the root isolate will also terminate all isolates spawned by that root isolate.
(root Dart isolate)also executes all responses for platform plugin messages, timers, microtasks and asynchronous I/O.
you cannot interact with the Flutter framework in any meaningful way on the secondary isolate. As non-root isolates are incapable of scheduling frames and do not have bindings that the Flutter framework depends on.
然而引用的dart isolate幾乎沒有用,我們想要的是isolate在flutter引擎中的表示,而不是isolate概念及使用文檔。但以上描述與flutter中用ui thread來運行RootIsolate::Run是一致的。
所以Isolate可以理解為(至少在flutter的表示中)一種特殊的線程,這個線程有自己的堆和棧(和普通線程一樣),但不能共享狀態,也就是不能加鎖來進行同步!RootIsolate 又是一個特殊的Isolate ,它的一個重要功能是調度和准備渲染幀,而具體的渲染工作由RootIsolate 交給GPU線程(應該存在另一個isolate實例)來做。
這個理解與在Engine::PrepareAndLaunchIsolate中調用了DartIsolate::Run是一致的,於是看RootIsolate創建流程:
RuntimeController::RuntimeController
DartIsolate::CreateRootIsolate
DartIsolate::DartIsolate
phase_ = Phase::Uninitialized;
DartIsolate::CreateDartVMAndEmbedderObjectPair
Dart_CreateIsolate
DartIsolate::Initialize
phase_ = Phase::Initialized;
DartIsolate::LoadLibraries
phase_ = Phase::LibrariesSetup;
在這里標注了一下DartIsolate::phase_的變化,以便能更好追蹤DartIsolate的狀態,同樣,結合之前的DartIsolate::Run調用序列:
::RunBundleAndSnapshotFromLibrary
::CreateIsolateConfiguration
IsolateConfiguration::CreateForAppSnapshot
AndroidShellHolder::Launch
...[async:ui_thread]Engine::Run
Engine::PrepareAndLaunchIsolate
IsolateConfiguration::PrepareIsolate
IsolateConfiguration::DoPrepareIsolate => AppSnapshotIsolateConfiguration::DoPrepareIsolate
DartIsolate::PrepareForRunningFromPrecompiledCode
Dart_RootLibrary
MarkIsolateRunnable
phase_ = Phase::Ready;
DartIsolate::Run
Dart_RootLibrary(), "main"
DartIsolate::InvokeMainEntrypoint
"dart:isolate._getStartMainIsolateFunction"
"dart:ui._runMainZoned"
phase_ = Phase::Running;
可見對phase_的檢查是符合預期的(phase_被置成Phase::Ready之前必須是Phase::LibrariesSetup)
以Dart_開頭的方法都是DartSDK的方法,分布在各種對象的各種方法中,但大體上我們知道了flutter中的DartIsolate是SDK中Dart_Isolate的封裝。
在調用入口方法之前(InvokeMainEntrypoint),先獲取了入口方法本身(user_entrypoint_function),從哪里獲取的?Dart_RootLibrary()。我們應該能猜出來這個RootLibrary應該就是我們編寫的Dart應用(main.dart所在的lib/目錄下那一坨),所以另外追蹤一下如何設置RootLibrary的,Dart_SetRootLibrary流程:
Shell::Create
DartVMRef::Create
DartVM::Create
DartVM::DartVM
DartUI::InitForGlobal
Dart_Initialize
DartIsolate::DartIsolateCreateCallback
DartIsolate::DartCreateAndStartServiceIsolate
DartServiceIsolate::Startup
Dart_SetRootLibrary
原來是在創建Shell前先創建了DartVM,在DartVM的構造函數時設置的,而且終於涉及到了那個一直被雪藏的類DartVM!
DartVM與DartVMRef
DartVM與DartVMRef是什么關系?按照字面及代碼注釋意思,是一個實例與強引用的關系,DartVM只能通過DartVMRef::Create來獲取實例
A reference to the VM may only be obtained via the |Create| method.
那么可以總結如下:
DartVMRef屏蔽了DartVM的創建DartVMRef保證進程全局只有一個DartVM實例及數據(DartVMData)DartVMRef線程安全的獲取DartVM實例
我覺得倒不如叫DartVMManager來的簡單明白,DartVMRef除了引用還干了這么多事...那個通過DartVMRef::Create方法來獲取實例的操作看着也比較別扭。
Dart虛擬機
目前的階段沒法深入虛擬機實現原理,加載機制,只能通觀概覽的了解一下它的特性。目測DartVM所做的工作其實並不多,主要是調用了DartSDK的各種API。
虛擬機分類
虛擬機先分為系統虛擬機(system vm)和應用虛擬機(process vm), 應用虛擬機又可分為字節碼虛擬機(bytecode vm)和源碼虛擬機(language vm),與JVM不同,DartVM是后面這種。
非字節碼
我們知道Dart虛擬機可以JIT(解釋執行)也可以AOT(編譯運行),這是被選作flutter開發語言的原因之一。可以肯定的是Dart虛擬機沒有基於字節碼,因為一旦用了字節碼指令,相關的復雜度其實是膨脹的,解釋可以參看這篇非常棒的為什么不用字節碼
,這種源碼虛擬機(language vm)其實和JS引擎有點類似。
非條件競爭
語言本身在創建之初的考慮就是避免這種鎖競爭的(Isolate機制)。
Adding support for sharing memory across threads in our VM would be pointless since the one language we know our VM will run doesn’t use it.
總之不采用字節碼是一種折衷(tradeoff),歸根結底還是為了保持簡單!
另外還可通過這篇文章了解DartVM,不過有點艱深。
