flutter: 加载与运行Dart
环境: 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,不过有点艰深。