外观
Stage06|JSB & Native Bridge
Lesson074|一个 Creator API 如何沿调用链进入 Native
Tags: #Creator2.x #Stage06 #CallChain #SourceCode #NativeDifficulty: ⭐⭐⭐⭐☆
真实问题:一个 API 到底该从哪里开始读源码
Native 保存失败时,开发者直接在 C++ 目录搜索“save”,得到几百个结果;也有人只看 TypeScript 声明,以为声明就是实现。
可靠路线是从公开入口开始,沿调用链逐层建立证据:
text
Creator API
→ 运行时封装
→ 绑定/桥接函数
→ C++ 类/平台适配
→ OS API
→ 回调/错误返回路径一、本课目标
本课不要求背引擎目录,而是建立一套可重复的源码定位方法:
看到一个 Creator API,如何确认它停留在 JavaScript 层,还是最终调用了 Native?
二、从公开 API 开始
例如:
ts
this.node.x = 100;
cc.audioEngine.playEffect(clip, false);
cc.sys.localStorage.setItem('key', 'value');不要先假定它们都经过同一条 JSB 链路。第一步是找到公开 API 的实现或属性定义。
三、源码定位五步法
text
1. 确认 Creator 版本和平台
2. 搜索公开 API 定义
3. 追踪包装函数、setter 或适配层
4. 判断是否进入绑定对象或原生模块
5. 找到最终平台实现和返回路径每一步都记录文件、函数和输入输出,不要只保存搜索结果截图。
四、先判断 API 类型
| API 类型 | 常见路径 |
|---|---|
| 纯 JS 数据处理 | JavaScript 内部完成 |
| Node / Component 状态 | JS 引擎状态,后续可能影响 Native 渲染 |
| 文件、音频、设备能力 | 常通过平台适配或 Native |
| Renderer / Texture | 可能经过渲染抽象和 Native 提交 |
| 网络请求 | 可能使用 Web API 或平台原生实现 |
这张表只用于确定搜索方向,最终必须以当前源码和构建目标验证。
五、追踪属性 setter
ts
this.node.x = 100;不能只搜索字符串 node.x。应继续查:
text
x 属性定义
→ setter
→ position 数据
→ Dirty Flag
→ Transform 更新
→ Renderer 使用某个 setter 可能完全在 JS 层修改数据,但结果在渲染阶段进入 Native。调用链不一定在赋值那一刻就跨边界。
六、追踪方法调用
ts
cc.audioEngine.playEffect(clip, false);可以沿着:
text
公开 API
→ JavaScript 包装
→ 平台选择
→ Native Audio Binding
→ C++ Audio Engine
→ Android / iOS 实现如果 Web 分支使用浏览器音频,而 Native 分支使用 C++/平台音频,同一 API 会在适配层分叉。
七、如何识别绑定入口
源码中可能看到:
- 注册原生函数或类。
se::Value、对象转换或参数校验。- 自动生成 binding 文件。
jsb、bindings、native、platform等目录或命名。- JavaScript 全局对象上的原生代理。
不要只根据文件名判断,关键是确认:
text
这个函数如何被注册到 JS 环境?
参数如何转换?
调用了哪个 C++ 方法?
结果如何返回?八、构建产物和源码不是一一对应
实际包中可能经过:
text
TypeScript 编译
JavaScript 合并和压缩
条件编译
自动绑定生成
C++ 编译和链接
平台打包所以堆栈中的函数名可能与开发源码不同。调试时需要:
- 保留正确符号和 Source Map。
- 确认构建模式。
- 使用同一版本引擎源码。
- 区分编辑器内置引擎与定制引擎。
九、案例:Native 文件写入失败
排查链路:
text
业务传入路径和文本
→ JS 包装是否选择正确平台分支
→ Binding 参数是否转换成功
→ C++ 文件 API 返回什么错误
→ 路径是否可写
→ Android / iOS 权限和沙盒规则只在 JavaScript 层反复 try/catch,无法解决原生路径或权限错误。
十、常见误区
误区一:搜索到同名函数就找到最终实现
它可能只是包装、声明、平台适配或生成代码入口。
误区二:所有属性修改都会立即跨 JSB
有些状态先留在 JS 层,渲染或同步阶段才进入 Native。
误区三:只看 JavaScript,不看构建平台
Web 和 Native 可能在适配层走不同实现。
误区四:直接修改引擎源码验证猜想
应先用日志、断点和最小实验确认调用链,再做受控修改。
十一、练习与答案
- 追踪公开 API 的第一步是什么?
- 为什么
node.x = 100不一定在 setter 中立即跨 JSB? - 如何确认一个 C++ 函数确实注册到了 JavaScript?
- Native 文件问题为什么要继续追踪平台实现?
答案:
- 锁定 Creator 版本、目标平台并找到 API 定义。
- setter 可能只更新 JS 状态,后续渲染同步才使用 Native。
- 查绑定注册、参数转换、调用目标和返回值路径。
- 路径、权限和沙盒规则由平台决定。
源码定位实验:建立最小调用链地图
选择项目已经使用的低风险 API,例如本地存储或设备信息。固定 Creator 2.4.x 与目标 Native 构建,不要为了实验新增平台功能。
| 层 | 记录内容 | 证据 |
|---|---|---|
| Creator | 公开方法与参数 | 类型/官方文档 |
| Runtime | JS 封装与返回 | 源码调用 |
| Binding | 注册名与转换 | 注册表/宏 |
| C++ | 方法与错误 | 固定版本源码 |
| Platform | OS API 与状态码 | 平台文档/日志 |
“文件名看起来相关”不是调用证据,至少要找到符号引用或目标平台日志对应。
APITrace.ts
ts
const { ccclass } = cc._decorator;
@ccclass
export default class APITrace extends cc.Component {
run(name: string, action: () => void) {
const begin = Date.now();
cc.log('[api-trace] begin', name);
try {
action();
cc.log(
'[api-trace] returned',
name,
Date.now() - begin
);
} catch (error) {
cc.error('[api-trace] threw', name, error);
}
}
}异步 API 还要记录 callback/Promise 完成,而不是只记录入口返回。
如何先判断 API 类型
- 纯脚本层:状态机、缓存和参数校验可能不跨边界;
- 引擎对象封装:可能标记 Native 状态或访问代理;
- 平台能力:文件、权限、支付、传感器通常需要适配;
- 构建工具链:某些 API 只生成配置,不在运行时跨 JSB。
先分类再定位,搜索范围会小很多。
真实项目故障:Native 文件写入只在升级后失败
源码入口没有改变,平台路径却从旧的可写目录迁移到沙盒目录。修复需要:
- 重新追踪 platform adapter;
- 打印最终路径和系统错误码;
- 使用平台文档定义的可写目录;
- 异步完成后再提示成功;
- Web/Native 接口保持同一语义;
- 不把构建产物目录当运行时数据目录。
版本边界与练习答案
源码路径、绑定宏和生成文件会因 Creator 2.4.x 小版本/平台变化。固定版本、从公开入口反向追踪、用符号和运行时日志交叉验证。
- 为什么从 C++ 搜索业务词不可靠? 中间有封装、注册名、宏和平台适配,业务词可能不存在或重名。
- 声明文件能证明实现吗? 只能说明类型契约,不能证明运行时路径和平台行为。
- 同步返回与异步完成要分别记录什么? 入口是否提交成功、最终回调/Promise 是否完成及错误。
- API 追踪的最小证据是什么? 每层可定位的符号/引用,加上一次目标平台可复现日志。
十二、本课总结
text
定位调用链要从公开 API 开始,经过包装、适配、绑定和平台实现。
先确认版本和平台,再用源码、断点和实验验证。十三、下一课预告
text
Lesson075|JavaScript 对象与 C++ 对象如何绑定