主题聚焦:
- JS → 原生:
cordova.exec/ ArkTS 插件调用链- 原生 → JS:
onArkTsResult/ JS Proxy / 事件推送- 如何系统识别“哪一段链路坏了”,并高效排查
文章以问题解决为导向,代码量控制在约 3/10,其余用文字和图示讲清楚思路。
1. 双向桥接总览:两条主链路
在本项目中,原生与 Web 之间的通信主要有两条路:
- 链路 A(JS 调原生):
cordova.exec(service, action, args)→ Cordova Core → ArkTS 插件execute()。
- 链路 B(原生调 JS):
- ArkTS/Native 通过两种方式触发 Web 行为:
onArkTsResult(JSON, 'CoreHarmony', ...)→ 触发标准 Cordova 事件、回调。WebviewController.registerJavaScriptProxy(obj, 'gameNative', ...)→ 在 JS 中暴露window.gameNative,允许直接调用。
- ArkTS/Native 通过两种方式触发 Web 行为:
1.1 总体架构图
常见问题的集中点:
- service/action 对不上 → JS 调原生失败。
onArkTsResult消息体不规范 → Web 侧事件不触发或解析失败。- JS Proxy 注入时机不稳定 →
window.gameNative为 undefined。
2. 链路 A:JS 调原生常见问题
2.1 问题 1:cordova.exec 没反应 / 报 Class not found
现象:
- JS 执行
cordova.exec后,success/fail 都没触发,或者直接报 service/class 不存在。
排查 Checklist:
-
ArkTS 中插件是否注册:
// Index.ets 中 cordovaPlugs: Array<PluginEntry> = [ { pluginName: 'GamePlugin', pluginObject: new GamePlugin() } ]; -
JS 中
service字符串是否与pluginName完全一致:cordova.exec(success, fail, 'GamePlugin', 'toast', [/* args */]); -
execute(action, args, cb)是否返回了true,并在所有分支调用了cb.success/cb.error*。
定位方式:
- 在 JS 调用处、插件
execute内部、回调里各打一个console.log,逐段确认调用是否抵达。
2.2 问题 2:ArkTS 收到调用,但业务结果不回到 JS
原因:
- 插件内部业务逻辑复杂,有异常或分支没有调用 callback。
建议做法:
- 为每条返回路径都调用 callback:
- 成功 →
cb.success(result?)。 - 失败 →
cb.errorByString(message)或其它 error API。
- 成功 →
- 任何
try/catch中的catch分支也要记得调用 error 回调,否则 JS 端会一直等待。
3. 链路 B:原生调 JS 常见问题
3.1 方式 1:事件推送(onArkTsResult → Cordova 标准事件)
在本项目中,ArkTS 会通过 onArkTsResult 向 Core 推送事件,进而在 JS 侧触发标准 Cordova 事件,例如 pause、resume、backbutton 等:
export function pageShowEvent() {
let result: ArkTsAttribute = { content: 'resume', result: [] };
cordova.onArkTsResult(JSON.stringify(result), 'CoreHarmony', '');
}
常见问题:
content字段拼写错误(如写成resmue),导致 Core 无法识别事件类型。- JSON 结构不符合预期,JS 侧解析失败。
解决思路:
-
定义一处统一的
ArkTsAttribute结构,所有事件复用:interface ArkTsAttribute { content: string; // 事件名 result: ESObject[]; // 参数数组 } -
使用类型安全的构造函数或工具函数,避免手写字符串出错。
3.2 方式 2:JS Proxy(原生暴露对象到 window)
另一种常用方式是通过 registerJavaScriptProxy 在页面中注入 window.gameNative:
controller.registerJavaScriptProxy(jsObj as ESObject, 'gameNative', [], ['toast']);
常见问题:
- 注册时机过早/过晚 → JS 访问时仍为 undefined。
- 多次注册不同对象,覆盖或者污染状态。
典型解决方案:
-
在多个关键生命周期中尝试注册(
initialize/onPageEnd/onControllerAttached等),并加日志确认:console.log('[GamePlugin] register gameNative proxy'); -
JS 端通过封装函数 + 重试机制访问(参考前一篇 Proxy 注入排查文章)。
4. 典型“双向桥接”问题场景
场景 1:Web 想通知原生“游戏结束”,原生弹出原生弹窗
需求:
- Web 侧判断游戏结束 → 通知原生 → 原生弹出 ArkUI 对话框。
双向桥接设计:
- JS → 原生:
cordova.exec('GamePlugin', 'gameOver', [score])。 - 原生 → JS:可选地回调结果,或通过事件广播“已展示弹窗”。
常见坑:
- JS 传递参数为
{score: 123},ArkTS 中却用args[0]当作基本类型处理,导致解析错误。 - ArkTS 处理后忘记调用
cb.success,JS 侧一直等待。
场景 2:原生想实时把性能指标推送给 Web,做浮动监控面板
需求:
- ArkTS 侧定时采集 FPS/内存等数据,通过桥接推送给 Web,Web 页面显示浮动监控条。
设计:
- 原生定时调用
onArkTsResult,content='perf',result=[{fps, memory}]。 - Web 侧监听自定义事件或在插件层封装事件分发。
常见坑:
- 数据量过大或频率过高,导致 JSON 序列化/反序列化成为性能瓶颈。
- Web 端对消息结构假设过于严格,一旦新增字段就报错。
解决思路:
- 定义稳定的消息 schema,并允许 Web 端 tolerant parsing(只用到自己需要的字段)。
- 对高频数据采用节流/合并策略,而非每帧推送。
5. 双向桥接问题排查通用流程
我们可以把“双向桥接”所有问题统一归纳为一个排查流程,从 发起方 到 桥接层 再到 接收方:
flowchart TD
A[桥接相关功能异常] --> B{问题发生在 JS→原生 还是 原生→JS?}
B -- JS→原生 --> C[检查 cordova.exec 调用: service/action/args]
C --> D[检查 ArkTS 插件 execute 日志与 callback]
B -- 原生→JS --> E[检查 onArkTsResult/JS Proxy 注册日志]
E --> F[检查 JS 监听事件/访问 window.gameNative 的逻辑]
D --> G{链路已通但业务结果不对?}
F --> G
G --> H[聚焦业务逻辑/数据结构]
实践建议:
- 对每条桥接链路,至少在三处打点:
- 发起方(JS 或 ArkTS);
- 桥接层(Core/Plugin/Proxy 注册点);
- 接收方(回调/事件监听函数)。
- 按照“哪一层有日志、哪一层没日志”的方式,快速锁定问题所在层级。
6. 小结
- HarmonyOS + Cordova 的双向桥接本质是两条链路:
- JS 调原生:
cordova.exec→ Core → ArkTS 插件。 - 原生调 JS:
onArkTsResult事件推送 + JS Proxy 直接调用。
- JS 调原生:
- 绝大多数问题都是因为:
- 字符串配置对不上(service/action/content)。
- 注册/注入时机不稳定。
- callback/事件监听缺失或实现不完整。
- 建议将“双向桥接”当作一个 端到端的通信系统 来看待,而不是零散地改一两个函数,通过日志与流程图把责任边界划清楚,排查起来会轻松很多。
875

被折叠的 条评论
为什么被折叠?



