Skip to content

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 可能在适配层走不同实现。

误区四:直接修改引擎源码验证猜想 ​

应先用日志、断点和最小实验确认调用链,再做受控修改。


十一、练习与答案 ​

  1. 追踪公开 API 的第一步是什么?
  2. 为什么 node.x = 100 不一定在 setter 中立即跨 JSB?
  3. 如何确认一个 C++ 函数确实注册到了 JavaScript?
  4. Native 文件问题为什么要继续追踪平台实现?

答案:

  1. 锁定 Creator 版本、目标平台并找到 API 定义。
  2. setter 可能只更新 JS 状态,后续渲染同步才使用 Native。
  3. 查绑定注册、参数转换、调用目标和返回值路径。
  4. 路径、权限和沙盒规则由平台决定。

源码定位实验:建立最小调用链地图 ​

选择项目已经使用的低风险 API,例如本地存储或设备信息。固定 Creator 2.4.x 与目标 Native 构建,不要为了实验新增平台功能。

层记录内容证据
Creator公开方法与参数类型/官方文档
RuntimeJS 封装与返回源码调用
Binding注册名与转换注册表/宏
C++方法与错误固定版本源码
PlatformOS 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 小版本/平台变化。固定版本、从公开入口反向追踪、用符号和运行时日志交叉验证。

  1. 为什么从 C++ 搜索业务词不可靠? 中间有封装、注册名、宏和平台适配,业务词可能不存在或重名。
  2. 声明文件能证明实现吗? 只能说明类型契约,不能证明运行时路径和平台行为。
  3. 同步返回与异步完成要分别记录什么? 入口是否提交成功、最终回调/Promise 是否完成及错误。
  4. API 追踪的最小证据是什么? 每层可定位的符号/引用,加上一次目标平台可复现日志。

十二、本课总结 ​

text
定位调用链要从公开 API 开始,经过包装、适配、绑定和平台实现。
先确认版本和平台,再用源码、断点和实验验证。

十三、下一课预告 ​

text
Lesson075|JavaScript 对象与 C++ 对象如何绑定