环境: 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,不过有点艰深。
另:flutter 编译模式