在 C/C++ 程序中嵌入 Dart VM:Dart Engine API 加载与调用 Snapshot 完整实战指南

原创2026-09-25 19:57:02972 阅读
文章标签:编程语言编译器语言运行时标准库开发工具

在 C/C++ 程序中嵌入 Dart VM:Dart Engine API 加载与调用 Snapshot 完整实战指南

Dart Engine API(dart_engine.h)是 Dart SDK 为 C/C++ 宿主程序提供的一套轻量级嵌入接口,它允许在非 Dart 程序中直接加载 Kernel 或 AOT 快照、创建多个 Isolate 并调用其中的 Dart 函数,从而把 Dart 快照当作"共享库"来复用。本文以 runtime/engine/README.md 为主线,结合头文件与底层实现源码,系统讲解该 API 的定位、全部公开函数、消息调度与 Isolate 锁机制,并通过 samples/embedder 中的可运行示例给出从构建到调用的完整实战路径。

Dart Engine API 是什么

Dart Engine API 位于 runtime/engine/include/dart_engine.h,它提供了一系列额外的函数,用于嵌入 Dart VM,以及从 Kernel 快照和 AOT 快照中调用 Dart 函数。它的设计目标很明确:

  • 服务对象:需要在非 Dart 程序中复用既有 Dart 代码的宿主应用(例如用 C++ 写主程序、用 Dart 写业务模块的场景);
  • 使用方式:把 Dart 快照当作动态共享库——宿主进程可以基于一个或多个快照启动一个或多个 Isolate,并调用其中的 Dart 函数;
  • 定位:它不是一套完整的、功能齐全的 API,必须与 dart_api.h 配合使用。例如调用 Dart 函数、操作 Dart_Handle、管理 Scope 等仍然依赖 dart_api.h 的标准接口。

与直接使用 dart_api.h 相比,Engine API 主要带来三方面改进(官方 README 原文要点):

  1. 完整的 VM 初始化:包括核心库(core libraries)的初始化,使得 print 等基础能力开箱即用;
  2. 更简单的 Isolate 消息处理:封装了 isolate 与 scope 的管理,并提供可按 Isolate 定制、也可全局默认的消息调度器;
  3. 锁保护的进入/退出 Isolate:DartEngine_AcquireIsolate 会阻塞等待直到可以安全进入 Isolate,避免多线程并发进入同一 Isolate 时崩溃。

核心 API 全景图

全部公开函数都在 runtime/engine/include/dart_engine.h 中声明,并通过 runtime/engine/dart_engine_impl.cc 导出实现。下表是完整的 API 清单:

函数 / 类型 作用
DartEngine_Init(char** error) 初始化 Dart Engine(初始化 embedder 并部分初始化 VM),成功返回 true,失败时 error 携带错误描述(调用者负责释放)
DartEngine_Shutdown() 关闭引擎,停止所有 Isolate 并释放所有资源
DartEngine_SnapshotKind 快照种类枚举:DartEngine_SnapshotKind_Kernel 或 DartEngine_SnapshotKind_AOT
DartEngine_SnapshotData 快照数据描述结构,包含 script_uri、kind 及联合体(Kernel 缓冲区或 AOT 的 data/text 指针),足以创建一个 Isolate
DartEngine_CreateIsolate(snapshot_data, char** error) 根据快照创建并启动一个新的 Isolate,成功返回 Dart_Isolate,失败返回 NULL
DartEngine_AcquireIsolate(isolate) 阻塞等待直到 Isolate 可进入,然后进入该 Isolate;Dart_EnterIsolate 的安全替代
DartEngine_ReleaseIsolate() 退出当前 Isolate 并使其可被其他线程进入;Dart_ExitIsolate 的配套替代
DartEngine_HandleMessage(isolate) 为指定 Isolate 处理一条消息,封装了 isolate 与 scope 的进出管理
DartEngine_MessageScheduler 消息调度器结构体:schedule_callback(调度回调)+ context(透传上下文)
DartEngine_SetDefaultMessageScheduler(scheduler) 设置所有 Isolate 共享的默认消息调度器
DartEngine_SetMessageScheduler(scheduler, isolate) 为单个 Isolate 设置专属消息调度器(优先于默认调度器)
DartEngine_HandleMessageErrorCallback 消息处理出错时的回调类型(携带 Dart_Handle error 与目标 Isolate)
DartEngine_SetHandleMessageErrorCallback(callback) 设置消息处理错误的回调
DartEngine_DrainMicrotasksQueue() 排空当前活跃 Isolate 的微任务队列,要求已进入 Isolate
DartEngine_KernelFromFile(path, char** error) 从文件加载 Kernel 快照,返回 kind 为 Kernel 的 SnapshotData
DartEngine_AotSnapshotFromFile(path, char** error) 从文件加载 AOT 快照(作为动态库打开),返回 kind 为 AOT 的 SnapshotData

其中 DartEngine_KernelFromFile 与 DartEngine_AotSnapshotFromFile 的文档均特别说明:用户不应释放返回的 uri 和缓冲区——它们由引擎内部持有并统一在关闭时释放(详见下文"内部实现原理")。

生命周期管理:初始化与关闭

初始化

DartEngine_Init 是使用 API 前的必要步骤。从 dart_engine_impl.cc 可以看到它委托给单例 Engine::instance()->Initialize(error)。结合 engine.cc 的实现,初始化做了两件事:

  1. 调用 dart::embedder::InitOnce 完成 embedder 的一次性初始化;
  2. 通过 Dart_SetVMFlags 设置 VM 标志——当运行在预编译(AOT)运行时中时,会自动追加 --precompilation 标志。

值得注意的关键设计:Initialize 只是"部分初始化"VM。完整的 VM 初始化(Dart_Initialize)被推迟到用户创建第一个 Isolate 时才发生。原因在 engine.h 的注释中写得很清楚:在 AOT 模式下,VM snapshot 数据只有在用户加载 AOT 快照(启动第一个 Isolate)之后才能拿到,因此无法提前完成完整初始化。这一点在 engine.cc 中由 first_isolate_started_ 标志保证只执行一次。

关闭

DartEngine_Shutdown 会:

  • 将 is_running_ 置为 false;
  • 遍历所有 Isolate,依次加锁进入、清除 Dart_SetMessageNotifyCallback(nullptr)、删除持久化句柄、调用 Dart_ShutdownIsolate,再解锁(engine.cc);
  • 释放引擎持有的快照缓冲区:Kernel 缓冲区被 free,AOT 缓冲区则注释明确"无需释放"(因为它们归动态库所有),并释放每个快照的 script_uri(engine.cc)。

需要注意的是,engine.h 的注释强调:引擎一旦关闭,不应当被重新初始化。

快照数据模型:Kernel 与 AOT

数据结构

DartEngine_SnapshotData 是贯穿整个 API 的核心结构(dart_engine.h):

typedef struct DartEngine_SnapshotData {
  const char* script_uri;          // 快照唯一标识,会传给 Dart_CreateIsolateGroup(FromKernel)
  DartEngine_SnapshotKind kind;    // Kernel 或 AOT
  union {
    struct {                        // Kernel 模式
      const uint8_t* kernel_buffer;
      intptr_t kernel_buffer_size;
    };
    struct {                        // AOT 模式
      const uint8_t* snapshot_data;
      const uint8_t* snapshot_text;
    };
  };
} DartEngine_SnapshotData;

script_uri 是快照的唯一标识,在创建 Isolate 时会作为 Dart_CreateIsolateGroup 或 Dart_CreateIsolateGroupFromKernel 的参数传入。

从文件加载

Kernel 快照加载(engine.cc):DartEngine_KernelFromFile 的实现非常直白——把整个文件读入内存缓冲区,script_uri 用 "file://%s" 格式拼出。文件读取失败或读取字节数不符时返回错误字符串(含 errno 信息)。加载成功后缓冲区会被登记到 owned_snapshots_,由引擎在 Shutdown 时统一 free。

AOT 快照加载(engine.cc):DartEngine_AotSnapshotFromFile 通过 Utils::LoadDynamicLibrary 把 AOT 快照当作共享库打开,然后解析其中 kSnapshotDataCSymbol 与 kSnapshotTextCSymbol 两个符号,得到 snapshot_data 和 snapshot_text 指针。这印证了 README 中"将 Dart 快照当作共享库使用"的定位——AOT 快照本质上是可动态加载的库文件。

创建并启动 Isolate

DartEngine_CreateIsolate 是"快照 → 可调用 Isolate"的关键入口(engine.cc)。它的完整流程如下:

  1. 快照种类与运行时匹配校验:预编译(AOT)运行时只接受 AOT 快照,非预编译(JIT)运行时只接受 Kernel 快照,否则返回错误(源码中两个分支使用同一错误文本 "AOT Dart VM supports only AOT snapshots")。
  2. 首次启动时完成完整 VM 初始化:在 engine_lifecycle_ 互斥锁保护下调用 Dart_Initialize(仅第一次)。
  3. 初始化 Isolate 标志:通过 Dart_IsolateFlagsInitialize 获取默认标志。
  4. 按运行时选择创建路径:
    • AOT:调用 Dart_CreateIsolateGroup(script_uri, ...),用 strrchr(script_uri, '/') 推导库名作为名称参数;
    • Kernel:调用 Dart_CreateIsolateGroupFromKernel(script_uri, script_uri, kernel_buffer, kernel_buffer_size, ...)。
  5. 安装消息通知回调:Dart_SetMessageNotifyCallback(Engine::MessageNotifyCallback),把 Dart VM 的消息通知接入引擎内部调度。
  6. 初始化核心库:进入 Scope 后调用 bin::DartUtils::SetupCoreLibraries——正如 README 所说,这是"完整初始化"的一部分,没有这一步 print 都不会工作(engine.cc 的注释原话)。
  7. Kernel 模式额外设置根库:Dart_LoadScriptFromKernel 被再次调用,因为 kernel 快照的根库 URI 与 script_uri 无关,取决于构建快照时的路径。
  8. 保存 Isolate 元数据:查找 dart:isolate 库,建立持久化句柄(isolate 库与 _runPendingImmediateCallback 函数名),初始化该 Isolate 的调度器字段为空。
  9. 退出 Scope 与 Isolate,将 Isolate 登记到 isolates_ 列表,返回 Dart_Isolate。

进入与离开 Isolate:锁保护的 Acquire/Release

这是 Engine API 与 dart_api.h 差异最明显的一个点。官方 README 明确指出:因为 Engine API 使用自己的消息处理机制,所以必须用 DartEngine_AcquireIsolate / DartEngine_ReleaseIsolate 替代 Dart_EnterIsolate / Dart_ExitIsolate。

原因在于两者的行为差异:

  • DartEngine_AcquireIsolate 会先尝试获取该 Isolate 对应的内部互斥锁(每个 Isolate 在 Engine::IsolateData 中持有一把 Mutex,见 engine.h),获取到锁之前会阻塞等待,然后再调用 Dart_EnterIsolate(dart_engine_impl.cc);
  • Dart_EnterIsolate 则不等待——如果其他线程已经进入了同一个 Isolate,会直接崩溃。

对应的 DartEngine_ReleaseIsolate 先 Dart_ExitIsolate(),再通过 Engine::UnlockIsolate 释放锁,让其他线程可以进入(dart_engine_impl.cc)。

在多线程宿主(例如一个线程池要并发调用同一个 Dart Isolate 中的函数)场景下,这个"阻塞等锁"的语义是保证安全性的关键。

处理 Isolate 消息:三种方案对比

README 梳理了在 dart_api.h 中处理异步 Isolate 消息的两种传统方式,以及 Engine API 提供的第三种替代方案:

方案 机制 局限
Dart_MessageNotifyCallback 用户传入回调获得消息到达通知 处理消息仍需手动"进入 Isolate → 进入 Scope → Dart_HandleMessage → 退出 Scope → 退出 Isolate"
Dart_RunLoop 在独立线程上运行事件循环 运行后无法从其他线程进入 Isolate
DartEngine_HandleMessage(Engine API) 封装了 isolate 与 scope 的管理 需配合消息调度器,且只能处理"单条消息"

DartEngine_HandleMessage 的实现非常能体现"封装"二字(engine.cc):

void Engine::HandleMessage(Dart_Isolate isolate) {
  LockIsolate(isolate);
  Dart_EnterIsolate(isolate);
  Dart_EnterScope();
  Dart_Handle handle_result = Dart_HandleMessage();
  if (Dart_IsError(handle_result)) {
    // 调用用户错误回调,或打印错误日志
  }
  Dart_ExitScope();
  Dart_ExitIsolate();
  UnlockIsolate(isolate);
}

一次调用就把"锁、进入、Scope、Dart_HandleMessage、错误处理、退出 Scope、退出 Isolate、解锁"整套样板代码收敛了。调用者唯一要做的是:把 DartEngine_HandleMessage(isolate) 调度到合适的时机与线程执行——这正是消息调度器(Message Scheduler)的职责。

消息调度器:默认调度器与按 Isolate 调度器

调度器结构

typedef void (*DartEngine_ScheduleMessageCallback)(Dart_Isolate isolate, void* context);

typedef struct DartEngine_MessageScheduler {
  DartEngine_ScheduleMessageCallback schedule_callback;
  void* context;
} DartEngine_MessageScheduler;

调度器本身不负责执行消息处理,它只负责"调度"——每当某个 Isolate 有新消息时,引擎调用 schedule_callback(isolate, context),回调里只需安排一次 DartEngine_HandleMessage(isolate) 的执行(可以在本线程、独立线程、std::async 等任意方式)。

调度优先级与内部流程

引擎对消息通知的处理集中在 Engine::NotifyMessage(engine.cc):

  1. 先尝试获取 engine_lifecycle_ 锁并检查 is_running_——如果关停正在进行或已完成,直接放弃本次通知(这是防止关停期死锁的关键,详见下文);
  2. 查找该 Isolate 的专属调度器(DataForIsolate(isolate)->scheduler),若为空则回退到默认调度器(default_scheduler_);
  3. 若两者都为空,忽略消息;否则调用 schedule_callback(isolate, scheduler.context)。

因此优先顺序是:按 Isolate 设置的调度器 > 默认调度器。两个设置函数对应 dart_engine.h:

  • DartEngine_SetDefaultMessageScheduler(scheduler):影响所有未单独设置的 Isolate;
  • DartEngine_SetMessageScheduler(scheduler, isolate):只影响指定 Isolate。

关停期间的防死锁设计

NotifyMessage 中对 engine_lifecycle_ 的 TryLock 使用并非偶然。engine.cc 的注释描述了需要防范的经典死锁场景:

  • 一边,消息通知线程(正持有 PortMap::mutex_)想锁住一个 Isolate 去调度 DartEngine_HandleMessage;
  • 另一边,关停线程正持有该 Isolate 的锁、等待 PortMap::mutex_(发生在 Dart_ShutdownIsolate 内部)。

通过让 NotifyMessage 在拿不到 engine_lifecycle_ 锁时直接放弃调度(视作"关停进行中"),就切断了这个环。这是源码级可见的工程细节,也解释了为什么调度器回调必须"只调度、不阻塞"。

微任务队列与消息错误回调

手动排空微任务队列

正常情况下,引擎在每处理完一条 Isolate 消息后都会排空微任务队列。但有一种情况需要手动干预:当宿主通过 Engine API 直接调用进入 Dart 代码时,可能需要在调用返回后手动排空微任务队列。

DartEngine_DrainMicrotasksQueue() 要求调用者已处于活跃 Isolate 中,其实现(engine.cc)是对 Isolate 库上的 _runPendingImmediateCallback(源码中 kRunPendingImmediateCallback 常量)发起一次 Dart_Invoke。头文件还给出两条重要语义:

  • 如果某个微任务抛出异常,错误会返回给调用者,队列中可能仍有剩余条目;
  • 调用者应在处理完错误后继续排空队列。

消息错误回调

通过 DartEngine_SetHandleMessageErrorCallback 注册的回调,会在 Dart_HandleMessage 返回错误时被触发(签名见 dart_engine.h)。如果没有注册回调,引擎会退回到 Syslog::PrintErr 打印错误(engine.cc)。

实战示例:samples/embedder

官方在 samples/embedder 中提供了完整的可编译示例,覆盖了从最简单到多快照协作的全部典型用法。示例同时支持 Kernel 与 AOT 快照,取决于链接的是哪种运行时库变体。

重要前提:快照文件格式不稳定,运行示例所用的 dart 可执行文件必须与快照版本匹配,最稳妥的方式是从同一份 checkout 构建 SDK(见 docs/Building.md)。

run_main:最简调用流程

run_main.cc 是所有示例中最简单的一个:加载快照 → 创建 Isolate → 获取 Isolate → 进入 Scope → 调用 main 函数 → 退出。核心代码模式如下:

DartEngine_SnapshotData snapshot_data = AutoSnapshotFromFile(argv[1], &error);
CheckError(error, "reading snapshot");
Dart_Isolate isolate = DartEngine_CreateIsolate(snapshot_data, &error);
CheckError(error, "starting isolate");

DartEngine_AcquireIsolate(isolate);
Dart_EnterScope();
Dart_Handle main_args = ToDartStringList({"world"});
CheckError(Dart_Invoke(Dart_RootLibrary(), Dart_NewStringFromCString("main"),
                       1, const_cast<Dart_Handle*>(main_args.begin())),
           "calling main");
Dart_ExitScope();
DartEngine_ReleaseIsolate();
DartEngine_Shutdown();

注意:它不处理 Isolate 消息,因此无法运行带异步函数的 Dart 程序(samples/embedder/README.md 原文明确说明)。目标 Dart 代码是 hello.dart,其 main 通过 @pragma('vm:entry-point', 'call') 标记为可从宿主调用的入口点。

构建与运行命令(来自 samples/embedder/README.md):

# Kernel 快照
./tools/build.py --mode=release samples/embedder:run_main_kernel && \
  out/ReleaseX64/run_main_kernel out/ReleaseX64/gen/hello_kernel.dart.snapshot.

# AOT 快照
./tools/build.py --mode=release samples/embedder:run_main_aot && \
  out/ReleaseX64/run_main_aot out/ReleaseX64/hello_aot.snapshot.

run_timer 与 run_timer_async:两种消息调度器实现

run_timer.cc 演示了在独立线程上运行 Isolate 事件循环:ThreadedMessageHandler 用"队列 + 条件变量"实现了一个专用消息处理线程,其 ScheduleDartMessage 静态方法把 Isolate 压入队列并唤醒处理线程,然后注册为默认调度器:

ThreadedMessageHandler message_handler;
std::thread message_handler_thread(&ThreadedMessageHandler::Run, &message_handler);
DartEngine_SetDefaultMessageScheduler(
    {ThreadedMessageHandler::ScheduleDartMessage, &message_handler});

run_timer_async.cc 则展示了用 C++11 std::async 实现的自定义调度器——调度回调只有一行:

void ScheduleDartMessage(Dart_Isolate isolate, void* context) {
  std::ignore = std::async(DartEngine_HandleMessage, isolate);
}

两者的主流程一致:设置默认调度器 → AutoSnapshotFromFile 加载快照 → DartEngine_CreateIsolate 创建 Isolate → 调用 Call_startTimer(isolate, 1) 启动 Dart 侧定时器 → 等待 100ms → Call_stopTimer(isolate) → 读取 Get_ticks(isolate) 打印结果 → 停止消息线程 → DartEngine_Shutdown。

Call_startTimer、Call_stopTimer、Get_ticks 等函数来自 timer.h,这是通过 dart pkg/vm/tool/generate_entry_point_shims.dart 从 Dart 代码自动生成的入口点 shim(头文件注释给出了生成命令),封装了"获取 Isolate → 调用函数"的样板,是"把 Dart 模块变成 C/C++ 可调用库"的典型形态。

run_two_programs:多快照协作

run_two_programs.cc 演示了同时启动两个快照:先从 program1 快照的 Isolate 调用 getValue 拿到返回值,再把该字符串作为参数传入 program2 快照的 Isolate 调用 printValue。整个过程展示了:

  • DartEngine_CreateIsolate 可以被多次调用,创建多个相互独立的 Isolate;
  • 宿主可以在不同 Isolate 之间自由切换(DartEngine_AcquireIsolate/ReleaseIsolate 成对出现);
  • 调用结果通过 StringFromHandle(helpers.h 中的辅助函数)转成 C++ 字符串。

公共辅助设施

所有示例共用 helpers.h,其中值得关注的几个工具:

  • AutoSnapshotFromFile:根据 Dart_IsPrecompiledRuntime() 自动选择 DartEngine_AotSnapshotFromFile 或 DartEngine_KernelFromFile,让同一份 C++ 代码同时适配两种运行时;
  • CheckError:统一的 char* / Dart_Handle 错误检查与退出;
  • DartScope / IsolateScope:RAII 封装 Dart_EnterScope/ExitScope 与 DartEngine_AcquireIsolate/ReleaseIsolate;
  • WithIsolate(isolate, body):模板函数,把"进 Isolate + 进 Scope + 执行 + 退出"收敛为一行。

示例还包含 run_futures.cc(演示 Future 相关的异步调用)。从 samples/embedder/BUILD.gn 可以看到,由于 FFI 无法在 VM 模拟器上执行,run_futures 仅在 dart_target_arch == host_cpu 时构建。

构建目标与集成方式

Engine API 的构建目标定义在 runtime/engine/BUILD.gn 中,核心是两个 source_set(engine_jit_set 与 engine_aot_set),它们各自编译 dart_engine_impl.cc 和 engine.cc,并分别链接 JIT(libdart_jit)与 AOT(libdart_aotruntime)运行时,同时引入 common_embedder_dart_io 等依赖。

由此派生出四种链接形态:

目标 形态 用途
dart_engine_jit_shared 共享库 JIT/内核模式动态链接
dart_engine_jit_static 静态库(complete_static_lib) JIT/内核模式静态链接
dart_engine_aot_shared 共享库 AOT 模式动态链接
dart_engine_aot_static 静态库(complete_static_lib) AOT 模式静态链接

samples/embedder/BUILD.gn 中的 _all_configs 展示了宿主程序如何选择这些库:_kernel 后缀的示例链接 dart_engine_jit_shared/static 并以 application_snapshot 生成 Kernel 快照;_aot 后缀的示例链接 dart_engine_aot_shared/static 并以 aot_snapshot 生成 AOT 快照(gen_kernel_args 中的 --link-platform 用于链接平台库)。

内部实现原理速览

单例 Engine 与两把锁

Engine 是一个进程级单例(Engine::instance() 静态局部变量),因为 Dart VM 拥有全局状态,引擎实例必须唯一(engine.h 的注释明确说明)。它内部使用两把锁:

  • engine_lifecycle_:控制初始化/关停,并在关停期间阻止消息投递以防止死锁;
  • engine_state_:保护引擎状态数据(快照列表、已加载动态库、Isolate 数据表)。

此外,每个 Isolate 在 IsolateData 中还有一把独立的 Mutex,供 AcquireIsolate/ReleaseIsolate 使用(engine.h)。

消息通知的完整调用链

从 Dart VM 到用户调度器,消息的流转路径为:

Dart VM 内部发消息
  → Engine::MessageNotifyCallback(isolate)        // 注册到 Dart_SetMessageNotifyCallback
  → Engine::NotifyMessage(isolate)                // TryLock 防死锁 + 选择调度器
  → scheduler.schedule_callback(isolate, context) // 用户调度回调(例如压入队列 / std::async)
  → DartEngine_HandleMessage(isolate)             // 加锁进入 → 处理 → 退出解锁

这条链路把 README 中"调度器只需安排一次 DartEngine_HandleMessage 的执行"这句话完整落到了源码层面。

使用约束与注意事项

综合官方文档、头文件注释与源码实现,使用 Dart Engine API 时有以下几点需要牢记:

  1. 它不是独立 API:必须与 dart_api.h 配合使用,Dart_Handle、Scope、Dart_Invoke 等基础操作仍走标准接口;
  2. 快照类型与运行时严格匹配:AOT 运行时只能加载 AOT 快照,JIT 运行时只能加载 Kernel 快照,DartEngine_CreateIsolate 会做校验并报错;
  3. 进出 Isolate 必须成对:使用 DartEngine_AcquireIsolate 进入后,必须用 DartEngine_ReleaseIsolate 退出,不要混用 Dart_EnterIsolate/Dart_ExitIsolate,否则多线程场景会崩溃或死锁;
  4. 调度器只调度不阻塞:调度回调应尽快返回,实际的 DartEngine_HandleMessage 可以在任意线程执行;引擎在关停期间会丢弃消息通知,调度器设计上要能容忍这一点;
  5. 快照缓冲区由引擎管理:KernelFromFile/AotSnapshotFromFile 返回的 uri 与缓冲区归引擎所有,用户不得释放,引擎会在 DartEngine_Shutdown 时统一清理;
  6. 引擎不可重启:DartEngine_Shutdown 之后不应再尝试重新初始化;
  7. 异步代码需要消息循环:不注册任何消息调度器(或不用 DartEngine_HandleMessage 处理消息)的宿主无法运行带异步函数的 Dart 程序,请参考 run_timer 系列示例搭建自己的事件循环;
  8. 版本匹配:快照文件格式不稳定,宿主程序对应的 dart/SDK 构建版本必须与快照生成版本一致。

Dart Engine API 的完整文档与示例均可在仓库内继续深入:接口声明见 runtime/engine/include/dart_engine.h,核心实现见 runtime/engine/engine.cc 与 runtime/engine/dart_engine_impl.cc,可直接复用的完整示例位于 samples/embedder。

登录后查看全文
sdk