HarmonyOS 鸿蒙Next NDK调用JS function回调时,JS打印数据错误 ?

发布于 1周前 作者 h691938207 来自 鸿蒙OS

HarmonyOS 鸿蒙Next NDK调用JS function回调时,JS打印数据错误 ?

在JS层传入一个回调到NDK, 通过 napi_create_threadsafe_function 创建了线程安全函数对象进行维护,在使用自有库进行日志回调时, 通过 napi_call_threadsafe_function 创建任务到JS主线程。在JS主线程回调Callback的时候,出现了NDK回调前数据打印正常,回调后在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;

}

5 回复
有帮助的话帮忙点个关注哈

试下我给你写的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-&gt;file.c_str(), log-&gt;file.size(), &amp;callArg[1]);
napi_create_string_utf8(baseLogContext.env, log-&gt;func.c_str(), log-&gt;func.size(), &amp;callArg[3]);
napi_create_string_utf8(baseLogContext.env, log-&gt;data.c_str(), log-&gt;data.size(), &amp;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了。

参考链接:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/napi-data-types-interfaces-0000001820880433#ZH-CN_TOPIC_0000001857876573__napi_env

在HarmonyOS鸿蒙Next NDK调用JS function回调时,如果遇到JS打印数据错误的问题,这通常是由于数据类型不匹配、数据传递错误或上下文环境不一致导致的。以下是一些可能的排查方向:

  1. 数据类型检查:确保从NDK传递到JS的数据类型与JS函数期望的类型一致。例如,如果JS函数期望一个字符串,而NDK传递了一个整数,这会导致数据解析错误。

  2. 数据传递机制:检查NDK与JS之间的数据传递机制是否正确实现。确保使用了正确的API和方法来传递数据,并且没有遗漏任何必要的步骤。

  3. 上下文环境:确认JS函数执行时的上下文环境是否正确。有时,如果上下文被错误地修改或重置,可能会导致数据访问错误。

  4. 调试和日志:增加更多的调试信息和日志输出,以便更准确地定位问题所在。这可以帮助你理解数据在传递过程中的变化。

  5. 版本兼容性:检查NDK和JS框架的版本是否兼容。有时,版本不匹配会导致意外的行为。

如果问题依旧没法解决请联系官网客服,官网地址是:https://www.itying.com/category-93-b0.html。

回到顶部