HarmonyOS 鸿蒙Next NDK调用JS function回调时,JS打印数据错误 ?
HarmonyOS 鸿蒙Next NDK调用JS function回调时,JS打印数据错误 ?
// JS API
export const setBaseLogCallback: (cbFn: (level: number, file:string, line:number, func:string, data:string) => void) => void;
static void callTSBaseLog(napi_env env, napi_value jsCb, [[maybe_unused]] void context, void data) {
// 已经在JS线程
CallbackContext ctx= (CallbackContext) context;
LogCtx log = (LogCtx )data;
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, “callTSBaseLog”
“NDK base NDK-[%{public}s(%{public}s)]: %{public}s\n”, log->file.c_str(), log->func.c_str(), log->data.c_str());
napi_value callArg[5] = {nullptr};
napi_create_int32(baseLogContext.env, log->logLevel , &callArg[0]);
napi_create_int32(baseLogContext.env, log->line, &callArg[2]);
napi_create_string_utf8(baseLogContext.env, log->file.c_str(), log->file.size(), &callArg[1]);
napi_create_string_utf8(baseLogContext.env, log->func.c_str(), log->func.size(), &callArg[3]);
napi_create_string_utf8(baseLogContext.env, log->data.c_str(), log->data.size(), &callArg[4]);
napi_call_function(ctx->env, nullptr, jsCb, 5, callArg, nullptr);
delete log;
}
试下我给你写的demo,主要改进了NAPI参数的创建、空字符串场景的处理以及资源管理和异常处理机制,添加了详细的错误日志,可以发给我:
// 定义回调上下文结构体
struct CallbackContext {
napi_env env;
napi_ref callback;
};
// 线程安全函数回调处理
static void callTSBaseLog(napi_env env, napi_value jsCb, void* context, void* data) {
// 安全检查
if (!env || !jsCb || !context || !data) {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN,
"Invalid callback parameters");
return;
}
CallbackContext* ctx = static_cast<CallbackContext*>(context);
LogCtx* log = static_cast<LogCtx*>(data);
// 详细日志输出
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN,
"NDK base NDK-[%s(%s)]: %s\n",
log->file.c_str(), log->func.c_str(), log->data.c_str());
// 异常处理的作用域
napi_status status;
napi_value callArg[5] = {nullptr};
// 安全创建参数
status = napi_create_int32(env, log->logLevel, &callArg[0]);
if (status != napi_ok) {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN,
"Failed to create logLevel");
return;
}
status = napi_create_string_utf8(env,
log->file.empty() ? "" : log->file.c_str(),
log->file.size(),
&callArg[1]
);
if (status != napi_ok) {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN,
"Failed to create file string");
return;
}
status = napi_create_int32(env, log->line, &callArg[2]);
if (status != napi_ok) {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN,
"Failed to create line number");
return;
}
status = napi_create_string_utf8(env,
log->func.empty() ? "" : log->func.c_str(),
log->func.size(),
&callArg[3]
);
if (status != napi_ok) {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN,
"Failed to create function name");
return;
}
status = napi_create_string_utf8(env,
log->data.empty() ? "" : log->data.c_str(),
log->data.size(),
&callArg[4]
);
if (status != napi_ok) {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN,
"Failed to create log data");
return;
}
// 安全调用JS回调
napi_value global, result;
status = napi_get_global(env, &global);
if (status != napi_ok) {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN,
"Failed to get global object");
return;
}
status = napi_call_function(env, global, jsCb, 5, callArg, &result);
if (status != napi_ok) {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN,
"Failed to call JS callback");
}
// 安全释放资源
delete log;
}
// 创建线程安全函数的推荐方法
napi_value SetBaseLogCallback(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
napi_status status;
// 获取回调参数
status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
if (status != napi_ok || argc != 1) {
napi_throw_error(env, "EINVAL", "Invalid arguments");
return nullptr;
}
// 创建回调上下文
CallbackContext* context = new CallbackContext();
context->env = env;
// 创建持久化引用
status = napi_create_reference(env, args[0], 1, &context->callback);
if (status != napi_ok) {
delete context;
napi_throw_error(env, "ENOMEM", "Failed to create callback reference");
return nullptr;
}
// 创建线程安全函数
napi_threadsafe_function tsfn;
status = napi_create_threadsafe_function(
env, // 环境
args[0], // JS回调函数
nullptr, // 异步资源名称
napi_create_string_utf8(env, "BaseLogCallback", NAPI_AUTO_LENGTH, nullptr),
0, // 初始队列大小
1, // 线程数
context, // 上下文
[](napi_env env, napi_value js_cb, void* context, void* data) {
// 释放引用和上下文
CallbackContext* ctx = static_cast<CallbackContext*>(context);
napi_delete_reference(env, ctx->callback);
delete ctx;
}, // 最终回调
context, // 上下文
callTSBaseLog, // 线程安全函数回调
&tsfn // 输出线程安全函数
);
if (status != napi_ok) {
napi_throw_error(env, "ENOMEM", "Failed to create threadsafe function");
return nullptr;
}
// 保存线程安全函数句柄
SaveThreadSafeFunctionHandle(tsfn);
return nullptr;
}
总的来说,HarmonyOS是一款非常优秀的操作系统,期待它能在未来带给我们更多惊喜!
官方文档中有说明禁止缓存napi_env,禁止在不同Worker中传递napi_env。您用logContext.env = env;把env缓存到了全局变量中,并在子线程中使用:
napi_create_string_utf8(baseLogContext.env, log->file.c_str(), log->file.size(), &callArg[1]);
napi_create_string_utf8(baseLogContext.env, log->func.c_str(), log->func.size(), &callArg[3]);
napi_create_string_utf8(baseLogContext.env, log->data.c_str(), log->data.size(), &callArg[4]);
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>
其实您在创建的安全函数可以拿到系统传递的env,使用callJsLog(napi_env env, napi_value jsCb, [[maybe_unused]] void *context, void *data)中的env,上面的函数可以写成:
napi_create_int32(env, log->logLevel, &callArg[0]);
napi_create_int32(env, log->line, &callArg[2]);
napi_create_string_utf8(env, log->file.c_str(), log->file.size(), &callArg[1]);
napi_create_string_utf8(env, log->func.c_str(), log->func.size(), &callArg[3]);
napi_create_string_utf8(env, log->data.c_str(), log->data.size(), &callArg[4]);
napi_call_function(env, nullptr, jsCb, 5, callArg, nullptr);
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>
此外,您再func中的napi_get_reference_value(logContext.env, logContext.callbackRef, &jsCallback);也有使用缓存env的问题,但是您这个jsCallback对象并没有在后续使用,我们这边就暂时注释掉了。更改过后就没有出现[1229112862][file487918225:(data487918225)]: %{public}s的log了。
在HarmonyOS鸿蒙Next NDK调用JS function回调时,如果遇到JS打印数据错误的问题,这通常是由于数据类型不匹配、数据传递错误或上下文环境不一致导致的。以下是一些可能的排查方向:
-
数据类型检查:确保从NDK传递到JS的数据类型与JS函数期望的类型一致。例如,如果JS函数期望一个字符串,而NDK传递了一个整数,这会导致数据解析错误。
-
数据传递机制:检查NDK与JS之间的数据传递机制是否正确实现。确保使用了正确的API和方法来传递数据,并且没有遗漏任何必要的步骤。
-
上下文环境:确认JS函数执行时的上下文环境是否正确。有时,如果上下文被错误地修改或重置,可能会导致数据访问错误。
-
调试和日志:增加更多的调试信息和日志输出,以便更准确地定位问题所在。这可以帮助你理解数据在传递过程中的变化。
-
版本兼容性:检查NDK和JS框架的版本是否兼容。有时,版本不匹配会导致意外的行为。
如果问题依旧没法解决请联系官网客服,官网地址是:https://www.itying.com/category-93-b0.html。