原生与 Cocos 混合开发¶
本文介绍原生与 Cocos 混合开发场景下的 SDK 接入方式,适用于以 Android/iOS 原生页面为主、部分页面使用 Cocos Creator 的应用。原生宿主负责初始化观测 SDK,Cocos 只在自身页面可见期间接管 Cocos 层的 RUM View、自动采集和 Session Replay 画面捕获。
当前自动管理范围仅支持独立的 Cocos Activity 或 UIViewController。嵌入原生页面局部区域的 Cocos Surface 需要宿主手动管理 RUM View,不支持自动替换并恢复宿主页面 View。
接入边界¶
独立 Cocos 应用与原生和 Cocos 混合开发的应用使用不同的初始化入口:
| 场景 | 初始化入口 | 配置所有者 | 结束方式 |
|---|---|---|---|
| 应用整体由 Cocos 驱动 | guanceSdk.start() |
Cocos | guanceSdk.shutdown() |
| 原生 App 局部使用 Cocos | 原生 SDK 初始化后调用 guanceSdk.attach() |
原生宿主 | Cocos 页面调用 guanceSdk.leaveCocos() |
start() 与 attach() 互斥。进入 Hybrid 模式后,SDK 会拒绝 Cocos 侧的 mobile.start()、rum.start()、logger.start()、trace.start()、replay.start()、replay.stop() 和 shutdown() 调用,避免重复初始化或关闭原生宿主持有的 SDK。用户绑定、RUM 事件、日志写入和 Trace Header 等非初始化 API 仍可通过 bridge 使用。
本页 Replay 示例使用 withSessionReplay(baseSdk) 返回的 guanceSdk。请先按应用接入安装两个同版本 npm 包并运行 --replay;具体导入见下文接入 Cocos 生命周期。只采集 Cocos RUM、Log、Trace 时,可直接使用基础包的 attach({ autoTrack }),无需安装 Replay。
attach() 只接受 Cocos 捕获和自动采集配置:
guanceSdk.attach({
replay: {
captureFps: 1,
maxImageDimension: 720,
touchPrivacy: 'show',
},
autoTrack: {
scenes: true,
actions: true,
errors: true,
network: true,
},
});
attach() 会通过 FTCocosBridge 确认原生宿主已经初始化 SDK,并向原生全局上下文追加 sdk_package_cocos 版本信息;它不会传递上报地址、RUM App ID、采样率或重新初始化任何原生模块。
Replay 与 RUM 的采样决策、会话、数据存储和全局配置继续由原生 SDK 管理。
独立运行模式的 sessionSamplingRate、logSamplingRate、traceSamplingRate 及 Replay 百分比采样字段不属于 attach() 配置。Hybrid 模式仍在原生宿主初始化时设置采样,单位、字段名和小数支持以宿主所用的 Android/iOS Native SDK 为准;不要把 Cocos 的采样参数传给 attach() 或 enterCocos()。
touchPrivacy 只控制 Cocos 页面 Replay 是否记录触摸位置;原生页面继续使用宿主的 Session Replay 隐私配置。可选值、默认行为和隐私注意事项见 Cocos Creator 会话重放。
原生宿主需按SDK 版本与依赖要求配置与所安装 npm 包配套的原生 SDK。配套的 Android/iOS SDK 支持 Hybrid Replay,可在 Cocos 页面进入和离开时切换原生与 Cocos 画面捕获。
初始化原生宿主¶
Cocos npm/ZIP 安装包已经包含 TypeScript API、Creator 扩展以及 Android/iOS 的 FTCocosBridge,无需在宿主工程中额外添加初始化辅助类。可选 Replay 包提供 FTCocosReplayBridge 和图像处理代码,两个 Bridge 共享宿主使用的同一版本原生 SDK。
Android/iOS App 按各自原生 SDK 的接入要求初始化所需的 RUM、Logger、Trace 和 Session Replay,并确保在首次调用 guanceSdk.attach() 前完成初始化。具体配置参考:
iOS 宿主可按 SPM 接入说明切换依赖管理方式。宿主与 Cocos Bridge 应使用同一份原生 SDK,避免同时通过 CocoaPods 和 SPM 引入。仓库中的 Hybrid 示例读取同一份 cocos-sdk.config.json;更新本地扩展并重新生成 iOS 工程后,继续执行示例原有的 native:install 命令,安装器会通过 SPM 关联 Bridge 和 HybridSampleHost。宿主初始化及 Cocos attach() 流程保持不变。
对于独立的 Cocos Activity 或 UIViewController,无需为 Hybrid 接入改写原生 SDK 的初始化流程。Cocos 页面显示和离开时,分别调用 enterCocos() 与 leaveCocos() 完成 RUM View 和 Replay 捕获源的切换。
如果启用 Session Replay,原生宿主必须使用默认的原生 recorder 模式完成初始化,不要设置为 external-only;Cocos 侧仅在 attach() 中传入画面捕获配置。
Android 网络采集建议¶
HttpURLConnection 自动采集从 Android Agent 1.7.6-alpha03 搭配 Android Gradle Plugin 1.3.9-alpha01 开始支持,两个组件均需使用该版本或更高版本。
推荐开启 Cocos autoTrack.network: true,保持原生 HttpURLConnection 自动采集关闭(默认关闭)。原生宿主在初始化 RUM 前配置:
这样由 Cocos JS 层采集网络请求,同时保留原生 Resource 总开关,供宿主的 OkHttp 等请求使用。
冲突原因:Android 上的 Cocos JS XHR 底层会经过引擎的 HttpURLConnection 封装,Creator 2 对应 Cocos2dxHttpURLConnection,Creator 3 对应 CocosHttpURLConnection。同时开启 JS 和原生 HttpURLConnection 采集,会对同一次请求分别上报 Resource;两层没有跨层去重,同时注入 Trace Header 还可能导致 Resource 的 Trace 信息与服务端收到的请求头不一致。
iOS 网络采集建议¶
iOS SDK 1.6.8-alpha.5 新增 NSURLConnection 自动 Resource 采集与 Trace 关联,分别通过 FTRumConfig.enableTraceURLConnectionResource 和 FTTraceConfig.enableAutoTraceURLConnection 开启,默认均为 NO。这两个开关独立于用于 NSURLSession 的 enableTraceUserResource 和 enableAutoTrace;仅升级版本或打开原有开关不会启用 NSURLConnection 采集。
先确认当前引擎的实际请求实现,再选择采集层:
| iOS 请求路径 | 原生 Resource 开关 | 原生 Trace 开关 |
|---|---|---|
Creator 2.4.9 / 2.4.15 XHR:NSURLConnection |
enableTraceURLConnectionResource |
enableAutoTraceURLConnection |
Creator 3.8.8 XHR:NSURLSession |
enableTraceUserResource |
enableAutoTrace |
同一次 Cocos XHR 同时被 autoTrack.network 和对应的原生 Resource 采集覆盖时,JS 与原生层会分别上报 Resource,当前没有跨层去重。Trace Header 相同也不代表 Resource 已去重。NSURLConnection 采集会复用能识别的已有 Trace Header,不应直接套用 Android 双开后覆盖 Trace Header 的结论。
对于 Creator 2,推荐保留 Cocos autoTrack.network: true,保持原生专用开关关闭,并保留宿主独立的 NSURLSession 采集:
rumConfig.enableTraceUserResource = YES;
rumConfig.enableTraceURLConnectionResource = NO;
traceConfig.enableAutoTrace = YES;
traceConfig.enableAutoTraceURLConnection = NO;
如果由原生层采集 NSURLConnection,在原生 RUM / Trace 初始化前将上述两个专用开关设为 YES,同时关闭 Cocos autoTrack.network。如果仍需 JS 采集其他请求,可通过原生 resourceUrlHandler 按 URL 排除重叠请求(返回 YES 表示不采集 Resource),并单独确认 Trace 注入策略;Resource 过滤不会关闭 Trace 注入。
Creator 3.8.8 / iOS 的实际 XHR 对照中,JS 与原生双开时,3 次 HTTP 请求产生 6 条 Resource;只开任一层时均为 3 条。双开时两条记录的 Trace ID / Span ID 不同,服务端收到的是原生层注入的请求头。
对于 Creator 3.8.8,关闭 NSURLConnection 专用开关不能避免 NSURLSession 路径的重复。宿主已开启 NSURLSession 自动采集时,可将 Cocos autoTrack.network 设为 false,由原生层统一采集;如保留 JS 采集,则需避免原生 Resource 采集覆盖相同请求,同时保留宿主其他网络请求的采集需求。
业务手动调用 startResource / stopResource / addResource 时,也需要避开原生自动采集的同一请求。保存并调用未经 JS SDK 包装的 XHR 方法只能绕过 JS 自动采集,不能绕过原生采集。
验证时给每次请求添加唯一 request_id,将实际 HTTP 请求数与上传的 Resource 数逐一对应,并检查 HTTP 状态、耗时及 Trace ID / Span ID。页面显示请求成功不足以证明 Resource 自动采集成功。
接入 Cocos 生命周期¶
以下示例使用 Creator 3;Creator 2 需将基础包和 Replay 包的导入入口都改为 /creator2。
import { guanceSdk as baseSdk } from '@cloudcare/cocos-sdk/creator3';
import { withSessionReplay } from '@cloudcare/cocos-session-replay/creator3';
export const guanceSdk = withSessionReplay(baseSdk);
export function attachObservability(camera?: unknown): void {
if (camera) guanceSdk.setReplayCamera(camera);
guanceSdk.attach({
replay: {
captureFps: 1,
maxImageDimension: 720,
touchPrivacy: 'show',
},
autoTrack: {
scenes: true,
actions: true,
errors: true,
network: true,
},
});
}
export function enterCocos(viewName = 'Cocos'): void {
guanceSdk.enterCocos({ viewName });
}
export function leaveCocos(): void {
guanceSdk.leaveCocos();
}
在 Cocos 页面组件中绑定生命周期:
onLoad(): void {
attachObservability();
}
onEnable(): void {
enterCocos('Game');
}
onDisable(): void {
leaveCocos();
}
onDestroy(): void {
leaveCocos();
}
默认使用当前场景中找到的第一个 Camera。多 Camera 项目应把用于重放的 Camera 传给 attachObservability(camera),切换主 Camera 后再次调用 guanceSdk.setReplayCamera(camera)。
attach()、已经进入后的 enterCocos(),以及已经离开后的 leaveCocos() 都是幂等操作。销毁路径可以再次调用 leaveCocos(),用于覆盖异常退出或重复回调。
如果 recorder 切换失败,相关调用会抛出错误。排除 Native SDK 版本或初始化问题后,应再次调用当前生命周期方法完成进入或离开,不要改用 shutdown() 清理原生宿主持有的实例。
如果 autoTrack.scenes 为 false,enterCocos() 必须传入 viewName,SDK 会手动创建一个 Cocos View。开启场景自动跟踪后,viewName 用作进入时的初始 View,后续场景切换由场景名接管。
View 与 Replay 所有权¶
页面切换时的所有权顺序如下:
| 时机 | RUM View | Session Replay 捕获源 |
|---|---|---|
| 原生页面可见 | 原生自动或手动 View | 原生 recorder |
调用 enterCocos() |
停止可能存在的容器 View,启动一个 Cocos View | 先暂停原生 recorder,再开始 Cocos Canvas 捕获 |
| Cocos 页面可见 | Cocos 场景或指定 View | Cocos external recorder |
调用 leaveCocos() |
停止 Cocos View | 先停止 Cocos 捕获和在途帧,再恢复原生 recorder |
| 返回原生页面 | 原生自动或手动 View | 原生 recorder |
原生全屏弹层、登录页或其他页面即使没有销毁 Cocos 页面,只要完全覆盖 Cocos,也必须在展示前通过业务生命周期通知 Cocos 调用 leaveCocos();弹层关闭且 Cocos 再次可见后调用 enterCocos()。仅暂停 Cocos 渲染不能完成 RUM View 和 Replay 所有权转移。
同一时刻应只有一个有效 RUM View 和一个 Replay 捕获源。不要同时让原生自动跟踪与 Cocos 自动跟踪记录专用 Cocos 容器,也不要手动调用 guanceSdk.replay.start() 或 stop() 控制 attached Replay。
嵌入式 Cocos Surface¶
如果 Cocos 只占原生 Activity 或 UIViewController 的局部区域,当前 Native SDK 无法自动暂停并恢复宿主控制器的 View。该场景需要宿主关闭该页面的原生自动 View 跟踪,并自行使用手动 RUM API 管理宿主 View 与 Cocos View 的先后关系:进入前结束宿主 View,离开后重新启动宿主 View。
在 Native SDK 提供作用域化的 View 抑制和恢复接口前,不要仅根据 Cocos 引擎暂停、恢复回调推断 View 所有权。
验证接入¶
建议在 Android 和 iOS 真机上分别执行至少三轮 原生页面 -> Cocos 页面 -> 原生页面:
- 每次进入 Cocos 前调用
enterCocos(),离开或被完全覆盖前调用leaveCocos()。 - 检查原生日志中没有 SDK 重复初始化、缺少 Hybrid recorder 接口或 Replay 未以原生模式初始化的错误。
- 在 RUM 查看器确认每个时段只有一个 View,Cocos 场景没有同名原生容器 View。
- 打开 Session Replay,确认原生页面与 Cocos 页面连续可播放,边界没有双重画面、空白帧或离开后的 Cocos 残留帧。
- 检查 Cocos Action、Resource、Error,以及与 RUM 关联的 Log 和 Trace 数据。
常见错误:
| 错误 | 处理方式 |
|---|---|
The native host must install the native SDK before FTCocosSDK.attach() |
将原生 SDK 初始化提前到 attach() 之前 |
The native host must initialize the native SDK before FTCocosSDK.attach() |
将 iOS 原生 SDK 初始化提前到 attach() 之前 |
The native host owns ... in Hybrid mode |
删除 Cocos 侧的 SDK/RUM/Logger/Trace 初始化或关闭调用,仅保留原生初始化与 attach() |
Hybrid Replay is managed by enterCocos() and leaveCocos() |
不要直接调用 guanceSdk.replay.start() 或 stop(),改由 Cocos 页面生命周期控制 |
does not support Hybrid recorder switching |
按SDK 版本与依赖要求核对原生依赖,并重新生成、编译原生工程 |
Session Replay must be initialized by the native host in native recorder mode |
在原生宿主初始化 Replay,并关闭 external-only 模式 |
Call attach() before enterCocos() |
确保 Cocos 页面加载阶段先调用一次 attach() |
enterCocos.viewName is required when scene tracking is disabled |
传入 viewName,或开启 autoTrack.scenes |