靠契约咬合,不靠链接
唯一需要的符号是 node::Start 与稳定的 N-API 表面。桥不关心、也不需要知道
提供它们的是哪个 Node 小版本。
开源 · MIT · 四个仓库
不是 WebView、不是云函数、不是子进程。JNI/NAPI 引导件在应用自己的进程内调用
dlopen("libnode.so") + node::Start,并在 JavaScript 与
Kotlin / ArkTS 之间建立双向 JSON 桥。
ls -1 ~/node-bridge
每个平台都拆成两个包发布——小巧的桥(API 与加载器,走自己的 semver)与 运行时(只有引擎,版本号等于 Node.js 版本)。它们只在进程运行期相遇。
Kotlin API 与 JNI 引导件:NodeRuntime 状态机、说 JSON 的宿主桥、以及一个 NAPI addon。
运行期加载 libnode.so,从不链接它。
io.github.fellow99:node-bridge:0.2.0
查看仓库
打包成 AAR 的 Android 版 Node.js 引擎:交叉编译的 libnode.so(arm64-v8a)
加 libc++_shared.so,仅此而已。制品版本号等于 Node.js 版本。
io.github.fellow99:node-runtime:24.2.0
查看仓库
面向鸿蒙的 ArkTS API 与 NAPI 引导件,发布到 ohpm:NodeRuntime、HostBridge(Node → ArkTS)、
日志与崩溃取证,以及 dlopen 加载器。
@fellow99/node-bridge@0.2.0
查看仓库
以 HAR 承载的 OpenHarmony arm64-v8a 引擎:libnode.so +
libc++_shared.so 与版本常量。由云 CI 用 OpenHarmony LLVM-19 工具链交叉编译,
自身不提供任何 ArkTS API。
@fellow99/node-runtime@24.2.0
查看仓库
nm -D libnode_bridge.so | grep Start
桥只是一个加载器和一个说 JSON 的宿主。它从不链接引擎,而是运行期用
dlopen 找到 libnode.so、经
node::Start(int, char**) 进入。引擎本身就是一个普通共享对象——
ET_DYN、无 PT_INTERP。
NAPI addon 故意保留未定义的 napi_* 符号(-z undefs),
运行期再对着已加载的引擎(RTLD_GLOBAL)解析——用的永远是 Node 自己的
N-API 实现。换 Node 版本,不必重编桥。
libnode_bridge.so加载器——只链接 log/dl;dlopen + dlsymlibnode_host_client.soNAPI addon,经 process.dlopen 加载libnode.soNode.js + V8,来自运行时包bootstrap.js · host.jsJS 载荷,从应用 assets 物化到沙箱
唯一需要的符号是 node::Start 与稳定的 N-API 表面。桥不关心、也不需要知道
提供它们的是哪个 Node 小版本。
约半 MB 的桥走自己的 semver;约 41 MB 的引擎,版本号就是 Node 版本。 发桥的修复不用碰引擎,反之亦然。
每台引擎都由 CI 从官方 Node 源码包交叉编译;SHA-256 锁进
runtime.lock.json,7 项 ELF 门禁检查 ET_DYN、
PT_INTERP=0、ABI 与导出的入口符号。
Idle → Starting → Running → Exited/Failed 是可观察的状态机;平台硬限制
一律上报给调用方,绝不粉饰。
./quickstart --platform
桥提供 API,运行时提供引擎,两者缺一不可——桥从不硬依赖某个具体的引擎构建。
dependencies {
implementation("io.github.fellow99:node-bridge:0.2.0")
// 引擎,按 Node.js 版本钉死
runtimeOnly("io.github.fellow99:node-runtime:24.2.0")
}
// 先注册宿主能力,再启动(顺序不能反) val rt = NodeRuntime.getInstance(context) rt.host.register("fs.pickFolder") { """{"ok":true,"path":"/sdcard/Download"}""" } rt.start(NodeRuntimeOptions( entry = "bootstrap.js", appEntry = "selfcheck.js", )) // 空闲 → 启动中 → 运行中 → 已退出 / 失败
ohpm install @fellow99/node-bridge
ohpm install @fellow99/node-runtime@24.2.0
# 两个包都要装——桥不依赖运行时包
module.json5 中申请
ohos.permission.kernel.ALLOW_WRITABLE_CODE_MEMORY ACL;没有它
--jitless 仍可运行,但 WebAssembly 不可用。
import { NodeRuntime } from '@fellow99/node-bridge';
NodeRuntime.getInstance().start(ctx, {
scriptPath: '<沙箱路径>/payload/bootstrap.js',
});
node::Start 每进程只能执行一次,崩溃即终态;需要可重启就把 Node 放进独立的 :node 进程。2in1 / 平板(不做手机);Android 需 minSdk 24。两端都只提供 arm64-v8a。git clone --recurse-submodules
每一个引擎二进制都由 CI 从官方 Node.js 源码构建,发布前过 SHA-256 校验与 ELF 门禁。 总控工作区以 submodule 把四个仓挂在一起。