典型痛点:
- 想通过桥接一次性传“大对象”(复杂 JSON、列表、图片 Base64),结果性能骤降或直接 OOM。
- Web 与原生对消息结构理解不一致,导致解析错误、字段丢失或版本不兼容。
- 随业务演进不断给消息加字段,旧版本 JS/原生无法兼容。
本文从消息结构设计与序列化入手,结合本项目中
ArkTsAttribute、插件参数等实践,给出一套可落地的设计与排错方案。代码约占 3/10,其余用文字解释思路。
1. 双向桥接中的“消息”究竟长什么样
在本项目中,常见的桥接消息有两类:
-
ArkTS → JS:
-
使用
onArkTsResult(JSON.stringify(attr), 'CoreHarmony', '')发送:interface ArkTsAttribute { content: string; // 事件名,例如 'resume'、'perf' result: ESObject[]; // 参数数组,例如 [{fps: 60, mem: 123}] }
-
-
JS → ArkTS(插件参数):
-
使用
cordova.exec(success, fail, service, action, [args])发送:cordova.exec(success, fail, 'GamePlugin', 'toast', [{ message: 'Hi' }]);
-
1.1 消息流动示意图
flowchart LR
subgraph JS
J1[cordova.exec args]
J2[事件监听回调参数]
end
subgraph Bridge
B1[Core / 序列化]
end
subgraph ArkTS
A1[execute(action, args)]
A2[onArkTsResult(JSON)]
end
J1 --> B1 --> A1
A2 --> B1 --> J2
设计得当时,这些“消息”应该满足:
- 结构稳定、可演化;
- 数据量可控,避免一次传太多;
- 双端解析简单、不容易踩坑。
2. 大对象直接塞进桥接的风险
2.1 性能与内存问题
- 序列化开销:
- 大对象需要 JSON 序列化(ArkTS/JS 端),会占用 CPU;
- 内存占用:
- 传递多层嵌套对象、长字符串(尤其是图片 Base64)时,桥接层和两端同时持有多份数据副本;
- 带宽限制:
- 在线资源场景中,大 JSON/长字符串还会占用网络带宽。
2.2 压力示意
flowchart TD
A[大对象(JSON/大数组/Base64图片)] --> B[序列化 JSON.stringify]
B --> C[跨语言/进程传输]
C --> D[反序列化 JSON.parse]
D --> E[JS/ArkTS 处理]
如果每一层都对同一份数据做拷贝/解析,很容易形成性能瓶颈甚至内存峰值过高。
3. 消息结构设计的实践建议
3.1 使用“轻消息 + 重数据”的模式
不要在桥接消息里塞入全部数据本体,而是:
- 在消息中传递:
- ID、类型、版本号、小段摘要;
- 重数据(大数组/二进制)存放在:
- 本地缓存文件或数据库;
- 远端接口,由对方按需获取。
示例:原生采集性能日志,不要一次把几千条数据通过 onArkTsResult 推给 JS,而是:
-
消息中只告诉 JS:
{ "content": "perf_snapshot", "result": [{"id": "snap-2024-03-21-001"}] } -
JS 收到后再根据
id调用接口或本地缓存获取详细数据。
3.2 设计可扩展的 JSON 结构
约定:
- 固定字段:
type/version/payload; payload内按业务模块划分子字段;- 新增字段时保证向后兼容——旧版本忽略未知字段不会导致错误。
示例模板:
{
"type": "perf", // 消息类型
"version": 1, // 协议版本
"payload": {
"fps": 60,
"memory": 123,
"extra": { // 预留扩展
"device": "xxx",
"scene": "home"
}
}
}
4. ArkTS → JS 消息:onArkTsResult 的设计与排查
4.1 统一封装消息构造
不要到处手写 content + result,推荐集中封装:
function sendToJs(content: string, payload: ESObject): void {
const attr: ArkTsAttribute = { content, result: [payload] };
try {
cordova.onArkTsResult(JSON.stringify(attr), 'CoreHarmony', '');
} catch (e) {
console.error('[Bridge] sendToJs error:', e);
}
}
// 使用示例
sendToJs('perf', { fps: 60, memory: 123 });
好处:
- 统一控制 JSON 结构;
- 出错时日志集中,便于排查。
4.2 常见问题
- 消息体字段改名,JS 侧代码仍使用旧字段名;
JSON.stringify抛异常(循环引用、大对象超限等);content与 Core 期望的事件名不匹配。
排查方法:
- 先在 ArkTS 侧打印 JSON 字符串,再在 JS 侧打印收到的原始消息,两边对比;
- 出问题时优先判断:是“收不到”还是“收到但解析失败”。
5. JS → ArkTS 消息:插件参数设计与大对象处理
5.1 推荐参数格式
在 cordova.exec 中传入参数时,建议使用统一的对象结构,而不是随意拼数组:
cordova.exec(
success,
fail,
'GamePlugin',
'doSomething',
[{
type: 'saveSettings',
version: 1,
payload: {
enableSound: true,
difficulty: 'hard'
}
}]
);
ArkTS 侧解析示意:
execute(action: string, args: ESObject[], cb: CallbackContext): boolean {
if (action === 'doSomething') {
const msg = args?.[0] as ESObject;
const type = msg['type'];
const payload = msg['payload'] as ESObject;
// 基于 type/payload 做分发
// ...
cb.success();
return true;
}
return false;
}
5.2 大对象参数的替代策略
不要这样做:
// ❌ 直接把庞大配置/日志全部塞进去
cordova.exec(success, fail, 'GamePlugin', 'uploadLog', [{ bigLogBlob: veryLargeString }]);
推荐做法:
- 使用“ID + 分片”模式:
- 先把大对象分片写入本地(如 Web 侧 IndexedDB、原生沙箱文件),再通过桥接传递
id和元数据; - ArkTS/JS 根据 id 分步获取或处理数据。
- 先把大对象分片写入本地(如 Web 侧 IndexedDB、原生沙箱文件),再通过桥接传递
6. 典型问题场景与解决思路
场景 1:偶发 JSON.parse 失败,提示 Unexpected token
现象:
- JS 侧在处理
onArkTsResult消息时抛出JSON.parse异常,错误内容为Unexpected token。
可能原因:
- ArkTS 侧构造 JSON 时拼接了额外的日志/前缀,导致非纯 JSON;
- 消息体中包含非 UTF-8 可打印字符或被截断。
处理建议:
-
保证
onArkTsResult只传递纯 JSON 字符串; -
使用严格的构造函数,而不是字符串拼接:
const json = JSON.stringify(attr); // 避免手工拼接 -
在异常发生时打印原始字符串样例,结合长度检查是否被截断。
场景 2:同一个消息结构在不同版本 JS/原生间不兼容
现象:
- 新版本 ArkTS 增加了字段或修改了字段名;
- 旧版本 JS 无法正确解析或直接报错。
解决思路:
-
在消息中加入
version字段; -
JS 侧根据
version做兼容性处理:function handlePerfMessage(msg) { const v = msg.version || 1; if (v === 1) { // 旧版本结构 } else if (v === 2) { // 新版本结构 } } -
尽量避免删除/重命名字段,更多使用“新增字段 + 默认值”的方式演进。
7. 综合排查流程图
当你在桥接过程中遇到“消息相关”的问题(解析失败、数据丢失、性能异常等),可以按以下流程排查:
flowchart TD
A[桥接消息异常] --> B{问题发生在 ArkTS→JS 还是 JS→ArkTS?}
B -- ArkTS→JS --> C[打印 ArkTS 侧 JSON.stringify 结果]
C --> D{JS 能收到原始字符串吗?}
D -- 否 --> D1[检查 onArkTsResult 调用与 Core 通路]
D -- 是 --> D2[检查 JS JSON.parse/字段名/版本兼容]
B -- JS→ArkTS --> E[打印 JS 调用参数与 插件 execute 日志]
E --> F{ArkTS 能解析 args 吗?}
F -- 否 --> F1[检查参数结构、类型转换、字段名]
F -- 是 --> F2[关注业务逻辑/大对象处理]
D2 --> G{消息是否包含大对象?}
F2 --> G
G -- 是 --> G1[考虑轻消息+重数据、分片/ID 方案]
8. 小结
- 双向桥接中的“消息”设计,是混合架构可维护性的关键:
- 结构要稳定、可演进:type/version/payload 三段式结构是常用实践;
- 数据量要可控:大对象尽量转为“引用 + 分片”模型;
- 编码要显式、集中:统一的构造/解析函数比到处手写 JSON 更安全。
- 当遇到解析失败或性能问题时,不要只在一端加
try/catch,而要沿着“ArkTS → Core → JS”或反向链路,把每一层的原始消息打印出来,对比差异,这样才能稳稳地把问题抓出来。
919

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



