如何在宿主应用中集成 Lynx Node-API addon 并在页面中调用 requireNodeAddon
2026/9/15 22:10:14 网站建设 项目流程

如何在宿主应用中集成 Lynx Node-API addon 并在页面中调用 requireNodeAddon

【免费下载链接】lynxEmpower the Web community and invite more to build across platforms.项目地址: https://gitcode.com/GitHub_Trending/lynx10/lynx

Lynx 提供了一组实验性(experimental)的运行时集成点,允许宿主应用把 Node-API addon 暴露给 Lynx 页面。开源的 Explorer 示例应用实现了一套完整的宿主侧集成模式:示例模块LynxNodeAPIModule加上共享原生加载器 LynxNodeAPI.cc,页面 JS 通过requireNodeAddon("<addon>")按名称请求 addon,加载器在原生侧完成加载与初始化,把 addon 的 exports 发布到 JS 全局对象__lynx_node_addon_exports__上。

本文围绕这条链路展开:准备运行时依赖、把 addon 二进制打进宿主应用、注册宿主模块、为页面开启开关,最后在页面中调用requireNodeAddon并确认 exports 可用。Android 和 Harmony 是打包步骤文档最完整的两个平台,作为主路径;Apple 平台走静态链接,作为可选分支简要说明。

准备条件:SDK 版本与平台运行时依赖

集成功能要求Lynx 3.9.x SDK 基线(见 Lynx Node-API Addons 的 "Integration Prerequisites" 一节)。各平台的运行时依赖如下:

平台运行时依赖
AndroidPrimJS 3.9.x 运行时 + 匹配的libnapi_adapter.so
HarmonyPrimJS 3.9.x 运行时 + 匹配的libnapi_adapter.so
iOSPrimJS 3.9.x 运行时 + 最新兼容版本的LynxWeakNodeAPI

iOS 还有两项额外要求:

  • 应用启动时必须先一次性把 PrimJS 安装到LynxWeakNodeAPI桥接层,之后才能使用 Explorer 环境;
  • 如果同时通过 CocoaPods 集成PrimJSLynxWeakNodeAPI,当前必须开启generate_multiple_pod_projects,否则两个库中同名的 Node-API 头文件会互相冲突。

Explorer 示例中这些前置依赖的落点(供对照):Android 的 PrimJS 依赖在platform/android/lynx_android/build.gradle,Harmony 的在 oh-package.json5,iOS 的在explorer/darwin/ios/lynx_explorer/Podfile,iOS 运行时桥接安装在explorer/darwin/ios/lynx_explorer/LynxExplorer/AppDelegate.mm

Lynx 本身不规定唯一的 addon 加载器实现,上面这些属于集成前提;模块名、方法名、加载方式、导出符号等留给宿主自定义。

把 addon 集成进宿主应用

addon 的命名规则各平台一致:<addon>去掉lib前缀和文件扩展名后的库基名。例如libsample.so对应requireNodeAddon("sample")

Android:打包为 lib .so

Android 采用动态库加载,addon 需要以lib<addon>.so形式进入应用的 native library 目录(见 Android Node-API Addons):

  • 发布集成:通过 Androidaar发布 addon,aar内包含jni/<abi>/lib<addon>.so,Gradle 会自动把 native 库打进应用;
  • 本地集成:把lib<addon>.so拷入jniLibs.srcDirs列出的目录。Explorer 示例默认的手动放置位置是explorer/android/lynx_explorer/src/main/jniLibs/<abi>/

<abi>取值如arm64-v8aarmeabi-v7ax86_64。如果 addon 二进制只覆盖部分 ABI,在explorer/android/gradle.properties中配置构建 ABI 列表,例如:

abiList=arm64-v8a

Harmony:har 包或本地目录投放

Harmony 通过har包把 addon 的.so带进应用包;本地开发也可以把 addon 共享库放到explorer/harmony/lynx_explorer/src/main/cpp/napi_addons/<abi>/,Explorer 的 CMake 构建会将其拷贝到 native 库输出目录(见 Harmony Node-API Addons)。

构建本地 Explorer 应用前,需要先为 Harmony 的 workspace、app 和 native 类型包安装 OHPM 依赖:

ohpm install --all cd lynx_explorer && ohpm install --all cd src/main/cpp/types/liblynx_napi_addon_loader && ohpm install --all

Apple 平台(可选分支):静态链接

Android/Harmony/Windows 使用动态 addon 二进制;iOS/macOS 偏好静态集成——把 addon 链接进宿主应用,并在构建中包含一次生成的addon_use.h头文件,让NAPI_USE保留 addon 的静态注册入口。具体步骤见各平台文档:iOS(podspec 与 xcframework 集成)、macOS(静态库集成)。Windows 的 app-local 打包见 Windows。

宿主侧:注册模块并绑定运行时环境

页面请求需要经宿主模块桥接到原生加载器。Explorer 示例中宿主侧要做两件事:注册名为LynxNodeAPI的页面可见模块,以及在运行时 attach 时把 runtime 专属的napi_env绑定下来。

Android:在模块适配层注册类(注意注册的是类而不是实例,SDK 会为每个页面注入LynxContext):

// explorer/android/lynx_explorer/src/main/java/com/lynx/explorer/modules/LynxModuleAdapter.java LynxEnv.inst().registerModule("LynxNodeAPI", LynxNodeAPIModule.class);

LynxNodeAPIModule.java 在类加载时载入原生加载库lynx_napi_addon_loader(优先走宿主/引擎提供的 loader,否则回退到System.loadLibrary),运行时 attach 回调把napi_envruntimeId存入静态映射。

Harmony:LynxNodeAPIModule.ets 是SendableLynxModule,通过@Sendable的 token 对象管理 env,requireNodeAddon最终调用原生库liblynx_napi_addon_loader.so暴露的requireNodeAddonByToken(token.id, addonName)

各平台注册与桥接的完整文件清单(Android/iOS/Harmony/macOS/Windows)在 Lynx Node-API Addons 的 "Example Files" 各小节中列出,可对照阅读。共享加载器实现统一在 LynxNodeAPI.h 与 LynxNodeAPI.cc。

为页面开启 Node-API 集成

当前 Explorer 示例中,只有页面 URL 的 query 带上enable_napi_addon=1(或enable_napi_addon=true)时,Node-API 示例集成才会生效。例如:

  • Android:file://lynx?local://homepage.lynx.bundle?enable_napi_addon=1
  • Harmony:file://lynx?local://main.lynx.bundle?enable_napi_addon=1

Android 模块中的失败日志也印证了这一点:当拿不到napi_env时会打印requireNodeAddon failed: napiEnv missing/invalid ... Ensure enable_napi_addon is enabled and runtime attach callback has been received

在页面中调用 requireNodeAddon

示例模块LynxNodeAPI只暴露一个宿主方法requireNodeAddon(addonName)addonName必须与库基名匹配(去掉lib前缀与扩展名):

Android libsample.so -> requireNodeAddon("sample") Harmony libsample.so -> requireNodeAddon("sample") iOS 静态注册为 sample -> requireNodeAddon("sample") Windows sample.node / sample.dll -> requireNodeAddon("sample")

页面侧的调用形态来自 Explorer 的类型声明 typing.d.ts:

// typing.d.ts 中的声明 interface NativeModulesMap { LynxNodeAPI?: { requireNodeAddon(addonName: string): void; }; } declare var __lynx_node_addon_exports__: | Record<string, Record<string, (...args: unknown[]) => unknown>> | undefined;

注意requireNodeAddon返回void:它只是触发原生侧加载与初始化,结果不通过返回值传递,而是由加载器发布到全局对象上。页面 JS 的完整调用与读取路径如下:

// 1. 触发宿主加载 addon(前提:页面 URL 带 enable_napi_addon=1) NativeModules.LynxNodeAPI.requireNodeAddon("sample"); // 2. 加载器完成后,addon 的 exports 挂在全局对象上, // 键为调用 requireNodeAddon 时传入的名字 const sampleExports = globalThis.__lynx_node_addon_exports__ && globalThis.__lynx_node_addon_exports__["sample"];

结果验证与失败现象

成功判定:共享加载器初始化 addon 后,会确保全局存在__lynx_node_addon_exports__对象(若不存在则创建),并把该 addon 的 exports 对象以传入的名字为键写入其中(见 LynxNodeAPI.cc 的InitializeNodeModule)。因此验证方式是:调用后在页面 JS 中检查globalThis.__lynx_node_addon_exports__["sample"]是否为对象、其上的导出函数是否可调。

原生侧解析规则(Android/Harmony/Windows 动态加载路径):

  • 加载器按候选名依次尝试:Android/Harmony 上为lib<addon>.solib<addon>.node等,Windows 上为<addon>.node<addon>.dll,从进程默认库搜索路径解析;
  • 名称有强约束:只允许[A-Za-z0-9_.-],不允许/\..@:和路径分隔符,长度不超过 128。违反时直接输出Failed to load Node Addon: invalid name '%s'
  • 库文件全部候选都加载失败时输出Failed to load Node Addon '%s'. Last dlopen/dlsym error: %s(Windows 为Last LoadLibrary/GetProcAddress error: %lu),可据此判断是库不在搜索路径还是缺少依赖;
  • 库加载成功但没有napi_register_module_v1导出符号时,该候选会被卸载并继续尝试下一个;
  • 同一 addon 只加载一次,后续调用复用已加载的句柄并重新执行初始化。

Apple 静态路径:若napi_find_module_weak找不到注册表中的 addon,会输出Failed to find statically linked Node Addon '<name>'. Ensure it is linked into the app so its constructor can call napi_module_register before calling requireNodeAddon.——即检查 addon 静态库是否已链接进应用、addon_use.h是否被包含。

宿主模块层的日志(Android):原生加载库自身加载失败会打Failed to load native library: lynx_napi_addon_loaderruntimeId为 null 或napi_env缺失时分别有对应的requireNodeAddon failed: ...警告,其中后两条都提示确认enable_napi_addon已开启且已收到 runtime attach 回调。

限制与生产注意事项

  • 该能力与示例集成均为实验性:库命名、打包规则和宿主集成细节仍可能变化。
  • 示例中的动态加载策略仅用于演示。文档明确要求生产集成不要依赖默认库搜索路径(Windows 尤其如此),应:对addonName做白名单校验和/或限定固定基础目录、解析为该目录下的绝对规范路径、加载失败时返回可操作的诊断信息;Windows 上优先使用带受限搜索行为的LoadLibraryExW而非默认 DLL 搜索顺序。
  • 本文只覆盖"把已构建的 addon 集成进宿主应用"。编写 addon 本身需要使用最新版@lynx-js/weak-node-api,其文档负责头文件、注册宏、导出符号和构建配置;Explorer 文档不是编写 addon 的权威指南。

【免费下载链接】lynxEmpower the Web community and invite more to build across platforms.项目地址: https://gitcode.com/GitHub_Trending/lynx10/lynx

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询