-
Creator 版本:Cocos Creator2.4.11 目前应该后续的 2.4.x 版本都存在同样的问题
-
目标平台: Android
-
重现方式:必现
Android平台async函数捕获错误精确定位行数?
Android平台 Promise 捕获错误精确定位行数?
环境
- Mac
- Android Studio
- Chrome
- Cocos Creator 2.4.11
- Typescript
问题
如何精确的在原生Android平台像Web平台一样精确定位线上错误的行数?
代码
Helloworld.ts
import { GlobalErrorHandler } from “./GlobalErrorHandler”;
const {ccclass, property} = cc._decorator;
@ccclass
export default class Helloworld extends cc.Component {
@property(cc.Label)
label: cc.Label = null;
@property
text: string = ‘hello’;
protected onLoad() {
// 初始化错误监听器
GlobalErrorHandler.init();
}
start () {
// init logic
this.label.string = this.text;
this.testHandler();
}
private async testHandler(){
console.log(“testHandler”);
const data = {};
data.a.b = 1;
console.log("-------------");
console.log(“testHandler end”);
}
private async testHandler2() {
console.log(“testHandler2 end”);
}
private async testHandler3(){
console.log(“testHandler3”);
// await SplitFrameUtil.checkNextFrame();
// await this.testHandler4();
console.log(“testHandler3 end”);
}
}
注意: data.a.b = 1;其中这一行会报错 (data下没有a属性所以给data.a.b赋值的时候报错) ,其中这个函数为async 函数,本次的问题也是关于async函数的。
GlobalErrorHandler.ts
export class GlobalErrorHandler {
static init() {
// 捕获同步错误
window.onerror = (message, source, lineno, colno, error) => {
console.error(“同步错误:”, {
message,
source,
lineno,
colno,
error,
});
GlobalErrorHandler.outputCompleteError({
type: ‘同步错误’,
message,
source,
lineno,
colno,
error
});
};
console.log(“GlobalErrorHandler init success”)
if (‘onunhandledrejection’ in window) {
console.log(“当前环境支持 onunhandledrejection 事件”);
// 处理未捕获的Promise错误
window.addEventListener(‘unhandledrejection’, function(event) {
console.log(“未处理的 Promise 错误:”, event.reason);
const message = event.reason.message;
// const stack = event.reason.stack.replace(/\n/g, ‘
’);
const stack = event.reason.stack;
const jsErrorStack = JSON.stringify(event.reason);
console.log(“smile::GlobalErrorHandler m: l:54->”, jsErrorStack);
GlobalErrorHandler.outputCompleteError({
type: ‘未处理的 Promise 错误’,
reason: { type: event.type, message, stack }
});
event.preventDefault(); // 阻止默认处理
});
} else {
console.warn(“当前环境不支持 onunhandledrejection 事件”);
}
if (typeof process !== ‘undefined’) {
console.log(“当前环境支持 process.unhandledRejection 事件”);
process.on(‘unhandledRejection’, (reason, promise) => {
console.log(“捕获未处理的 Promise 错误:”, reason);
});
console.log(“当前环境支持 unhandledRejection success”)
}else {
console.warn(“当前环境不支持 process.unhandledRejection 事件”);
}
// 捕获已处理的 Promise 错误
window.onrejectionhandled = (event: PromiseRejectionEvent) => {
console.log("Promise 错误已被处理:", event.reason);
const message = event.reason.message
const stack = event.reason.stack
GlobalErrorHandler.outputCompleteError({
type: '已处理的 Promise 错误',
reason: {type:event.type,message,stack}
});
};
console.log("GlobalErrorHandler init success")
if(typeof jsb !== 'undefined'){
jsb && jsb.onError(function (location, message, stack) {
console.log("jsb 错误:", location);
console.log("jsb message:", message);
console.log("jsb stack:", stack);
});
}
}
// 输出完整的错误信息(可以进一步扩展为上传至错误监控系统等操作)
static outputCompleteError(errorDetails: { type: string; [key: string]: any }) {
// 这里可以扩展为将错误发送到远程日志或监控系统
console.log("\n完整的错误信息:", JSON.stringify(errorDetails, null, 2));
}
}
构建web平台和 Android平台

其中 Web平台报错
Android平台报错
问题分析
对比构建后的文件
我们看到 构建后的文件 内容都是完全一致的,但是web 可以精确报错到某一行,但是在Android平台 就无法精确了,我们可以看到在Android平台报错内容 只能看到 275行 而无法像 Web平台那样 看到 411行报错,而且最近的报错是 406行 ,这样就带来一个问题,线上游戏报错凡是async函数的报错都无法精确到报错到具体行数,如果函数内方法简单,相对来说还容易找到问题,但是对于一些行数很多的方案这样无法精确到哪一行。
自己做的一些探究
原生代码
我找到了代码 Creator/2.4.11/CocosCreator.app/Contents/Resources/cocos2d-x/cocos/scripting/js-bindings/manual/jsb_cocos2dx_manual.cpp
这个路径下:
se::ScriptEngine::getInstance()->setJSExceptionCallback([objFunc](const char *location, const char *message, const char *stack) {
se::ValueArray jsArgs;
jsArgs.resize(3);
jsArgs[0] = se::Value(location);
jsArgs[1] = se::Value(message);
jsArgs[2] = se::Value(stack);
objFunc->call(jsArgs, nullptr);
});
在这里添加打印发现:
se::ScriptEngine::getInstance()->setJSExceptionCallback([objFunc](const char *location, const char *message, const char *stack) {
// 输出异常的堆栈信息
CCLOG(“JS Exception caught!”);
CCLOG(“Location@@@: %s”, location);
CCLOG(“Message@@@: %s”, message);
CCLOG(“Stack@@@: %s”, stack);
se::ValueArray jsArgs;
jsArgs.resize(3);
jsArgs[0] = se::Value(location);
jsArgs[1] = se::Value(message);
jsArgs[2] = se::Value(stack);
objFunc->call(jsArgs, nullptr);
});
重新构建引擎,出包发现,是传进来的时候 就缺少了 完整的栈信息
然后找到了 这里:
polyfill/typescript.js
在这个后面添加catch,构建引擎,发现构建后的引擎文件被成功修改:
但是构建后项目内的bundle内的index.js 里面的promise被转化的代码 还是没有带catch,于是有了下面的猜想
解决方案求教
我这里想到两个解决思路:
方案一:完整传给jsb报错信息
既然在jsb 那边得到的就是不完整的,那么这个栈信息是如何丢失的?如何才能把完整的传过去?
方案二:如果在构建的时候把catch信息补充完整
这里指的补充完整是在转换的时候,由于补充后,会改变原来js代码的行数,会影响原来生成的sourcemap 所以,请教如何不使用插件在完成后更改,而是通过引擎模板或者构建设置更改?
最后:请教各位对引擎熟悉的,能否给出一个解决方案,可以精准定位线上报错信息位置的行数?
下面是demo 的完整代码 有愿意帮忙的 请大佬帮忙看看
DemoProject.zip (285.8 KB)
:GlobalErrorHandler m: l:54->”, jsErrorStack);







