外观
Stage06|JSB & Native Bridge
Lesson080|阶段复习:从 Creator API 定位到原生实现
Tags: #Creator2.x #Stage06 #JSB #Native #SourceCodeDifficulty: ⭐⭐⭐⭐⭐
阶段挑战:从一个公开 API 走到可解释的 Native 故障
本阶段的最终能力不是背 JSB 术语,而是面对:
text
Web 正常、Native 失败
Native 偶发崩溃
跨边界调用过多
回调晚于页面生命周期
后台线程直接更新 UI你能否从公开 Creator API 出发,画出执行链、确定线程/所有权、用最小实验和目标平台日志验证,而不是猜某个 C++ 文件。
一、本课目标
串联 Lesson073~Lesson079,完成 Cocos 公共核心的最后一段:
text
Creator API
→ JavaScript Engine
→ JSB / Binding
→ C++ Engine
→ Platform / GPU完成本课后,你应该能够对一个 Web/Native 差异、JSB 性能问题或原生崩溃建立分层排查路径。
二、Stage06 知识树
text
运行环境
├── TypeScript / JavaScript
├── Browser JavaScript Engine
└── Native Embedded JavaScript Engine
跨语言桥梁
├── Binding 注册
├── 参数和返回值转换
├── JS / C++ 对象映射
└── 异常和回调
生命周期
├── JavaScript GC
├── C++ 所有权 / 引用计数
├── Proxy 有效性
└── 异步任务
平台边界
├── 文件、音频、网络
├── 渲染和 GPU
├── 主线程与工作线程
└── Web / Native 差异三、从 API 到原生实现的标准流程
text
1. 锁定 Creator 版本、平台和构建模式
2. 找公开 API 定义
3. 追踪 JS 包装、setter 或适配层
4. 确认是否进入 Binding
5. 检查参数转换和对象映射
6. 找到 C++ Engine 方法
7. 找到最终平台实现
8. 追踪返回值、异常和回调线程
9. 检查两边对象生命周期
10. 用断点、日志和最小实验验证四、综合案例:Native 保存截图
业务目标:
text
截取当前画面
→ 保存文件
→ 返回路径
→ UI 显示成功可能链路:
text
JS 发起请求
→ Renderer / GPU 完成读回
→ Native 获取像素或文件数据
→ 工作线程编码图片
→ 平台文件系统保存
→ 投递主线程
→ Binding 转换路径和错误
→ JS 检查页面仍有效
→ 更新 UI需要检查:
- GPU 读回是否造成同步卡顿。
- 像素缓冲的内存峰值。
- 编码是否阻塞主线程。
- 写入路径是否可用。
- 回调是否来自正确线程。
- 页面关闭后回调如何处理。
- Native 和 JS 对象是否正确释放。
一个功能可能同时跨越 Renderer、Memory、IO、JSB、Thread 和 Lifecycle。
五、综合案例:Web 正常,Native 崩溃
排查树:
text
是否只在 Native 出现?
├── 是:检查平台分支和 Binding
│ ├── 参数类型是否一致
│ ├── Native 对象是否已销毁
│ ├── 回调线程是否正确
│ ├── 文件/权限是否不同
│ └── 是否有 C++ 崩溃堆栈
└── 否:先查公共业务和资源逻辑不要因为崩溃发生在 Native 就直接认定是 C++ 引擎 Bug;错误参数、过期代理和业务生命周期同样可能触发原生崩溃。
六、综合性能判断
看到 Binding 函数耗时时,应问:
text
调用了多少次?
每次传多少数据?
是否发生数组或字符串复制?
是否能批处理?
是否在目标设备复现?
优化后状态是否仍一致?跨 JSB 不是天然错误,无法解释调用规模的“JSB 慢”也不是有效结论。
七、阶段综合练习
问题:Native 下载器完成后偶发崩溃,通常发生在用户快速关闭下载页面时。
请从以下层次分析:
text
JavaScript 页面生命周期
JS callback 保存与释放
Binding 对象映射
C++ 下载任务生命周期
工作线程到脚本线程切换
请求取消和过期结果参考分析
text
页面发起任务并保存 requestId
→ Native 保存回调并启动下载
→ 页面退出,取消或使 requestId 失效
→ Native 完成后不直接从工作线程调用 JS
→ 投递脚本线程
→ 检查 ScriptEngine、callback、页面和 requestId
→ 有效才交付结果
→ 无论成功、失败、取消都释放 callback 和对象映射八、Stage06 面试题
1. JSB 是什么?
JSB 是 JavaScript 与 C++ 之间的绑定层,负责函数注册、参数转换、对象映射、返回值和生命周期协调。
2. 为什么频繁 JSB 调用可能较贵?
因为每次可能包含边界跳转、类型检查、对象查找、数据转换和复制,总成本会随次数和数据量累积。
3. JS 对象被 GC 后 C++ 对象一定销毁吗?
不一定,两边由不同生命周期模型管理,需要绑定层和所有权策略协调。
4. 为什么 Native 线程不能随意调用 JS?
JavaScript Engine 通常要求在指定线程和正确上下文执行,错误线程会导致竞态和崩溃。
5. Web 正常、Native 异常应如何排查?
从平台适配、绑定转换、Native 对象、线程、文件权限、图形 API 和生命周期差异逐层检查。
6. 如何追踪一个 Creator API?
锁定版本和平台,从公开 API 进入包装、适配、Binding、C++ 和平台实现,再追踪返回和生命周期。
阶段综合实验:NativeCapabilityLab
使用项目已有的 Native 能力与 Creator 2.4.x 构建流程。不要为了课程手工修改引擎源码、生成绑定文件或伪造原生资源。
实验模块
text
RuntimeProbe:Web/Native 环境
APITrace:公开入口时间线
Capability:文件/音频/网络最小调用
Lifecycle:取消与 session
Thread:后台结果回主线程
Batch:逐项与批量边界统一日志格式
ts
function nativeTrace(
name: string,
stage: string,
session: string,
extra?: any
) {
cc.log(
'[NATIVE-TRACE]',
name,
stage,
'session=', session,
extra || ''
);
}每条记录至少有:
text
api
stage(submit/callback/error/cancel)
session
timestamp
platform
thread(若可取得)综合步骤
- Web 运行,记录能力结果。
- Native 构建运行,记录同一动作。
- 从公开 API 找到 Runtime 封装。
- 用固定版本源码搜索绑定注册。
- 记录参数转换和回调方向。
- 关闭页面/切账号,验证旧 session 结果被拒绝。
- 测 1/100/1000 次调用与批量方案。
- 高并发后台回调,确认主线程队列有上限。
综合案例:Native 保存截图
需求拆解
text
Renderer 生成截图数据
→ 选择 Web/Native 存储能力
→ 可能有大字节复制
→ 后台写入
→ 回调成功/失败
→ 页面显示结果风险
- 截图数据过大造成复制和峰值;
- Native 文件路径与 Web URL 不同;
- 写入线程不能阻塞渲染;
- 页面关闭后回调失效;
- 用户权限/沙盒;
- 同名写入竞态。
验证
小图、中图、大图分别测;Web/Native 冷缓存测;关闭页面/退出账号测;记录 submit 到 callback、内存峰值和最终文件语义。
不要把“保存成功”定义为回调已提交;必须确认平台写入完成和下一次读取。
阶段故障定位树
text
API 不存在
→ 平台/版本/构建能力
API 返回错误
→ 参数/权限/路径/资源/平台状态
回调不回来
→ 线程/生命周期/取消/网络/队列
回调回来但崩溃
→ Native handle/线程归属/ABI/旧代理
功能正确但慢
→ JSB 调用频率/复制/批量/主线程提交
Web/Native 不一致
→ 能力适配/资源布局/安全策略/生命周期阶段综合练习与答案
练习一
每帧读取 1000 个 Native Node 的位置,如何改造?
答案: 先用纯 JS/模拟/真实 Native 对照证明边界成本;减少读取对象,只同步变化/可见对象;优先批量 API 或 Native 侧计算,按帧率降频;验证坐标时序与视觉。
练习二
页面关闭后 Native 下载完成回调仍返回,怎样保证安全?
答案: 关闭时取消底层任务(若支持),递增 generation/session;回调只提交到主线程;提交前验证 session、Node/代理有效性和资源所有权。
练习三
Web 保存设置成功,Native 失败,排查顺序?
答案: 确认平台环境与能力实现;输出最终沙盒路径/权限错误;检查构建配置和 Native API;比较同步/异步完成;加回退与错误提示,不使用 Web 绝对路径。
练习四
Native 回调每秒 5000 条,UI 只需显示进度,如何设计?
答案: Native/后台聚合,主线程按显示频率采样最后值;队列有上限和 session;UI 只在可见且值跨阈值时刷新,记录丢弃策略。
练习五
为什么不能仅靠 JS GC 解决 Native 泄漏?
答案: Native 可能由引用计数、回调、线程或平台句柄持有;JS 不可达不代表 Native owner 归还。必须明确跨边界释放协议。
Stage06 能力验收
你应该能够:
- 画出 Web/Native/JSB/C++/平台分层。
- 从公开 API 建立源码调用链证据表。
- 区分 JS Proxy、Native Object、Platform Handle。
- 估算跨 JSB 调用的次数、复制和批量收益。
- 设计 GC 与 Native 引用计数的断环路径。
- 为 Web/Native 能力建立适配层和回退。
- 保证后台回调回到正确线程与 session。
- 形成带版本、设备、日志和错误路径的 Native 故障报告。
版本边界与官方入口
Stage06 以 Creator 2.4.x Native 工作流为主。绑定注册、JS Engine、Cocos2d-x 和平台线程实现必须固定源码/构建版本;不能把某一平台观察推广为全部 Native。
九、Stage06 总结
text
JavaScript Engine 执行业务脚本。
JSB 连接 JavaScript 和 C++。
对象绑定包含身份、转换和生命周期。
跨边界成本取决于次数和数据量。
Web 与 Native 有不同运行环境。
后台任务必须安全交付到脚本线程。
源码定位必须绑定版本、平台和构建模式。Stage06 完成后,你应该从“Creator API 使用者”进一步成为:
能沿着 JavaScript、Binding、C++ 和平台层建立证据链的开发者。
十、下一阶段预告
Stage06 完成,下一阶段进入:
text
Stage07|Modern C++ Foundation
Lesson081|从 .cpp 到可执行文件:预处理、编译与链接下一阶段将用 Modern C++ 补齐阅读 Native 引擎和服务端代码需要的语言、内存、所有权、并发与工程基础。
