HarmonyOS鸿蒙Next中如何在Native侧C++子线程直接调用ArkTS接口,不用通过ArkTS侧触发回调

HarmonyOS鸿蒙Next中如何在Native侧C++子线程直接调用ArkTS接口,不用通过ArkTS侧触发回调 如题,举例:

项目中使用了官方的 Native 库:

#include "network/netstack/net_websocket.h"

这是一个WebSocket库,连接websocket后如果触发OnMessage回调,想要将收到的消息发到 ArkTS 侧来显示,反正我想到的最直接的方式是直接调用 ArkTS 中的一个方法回调,但当前的版本并不支持,下面是官方QA的回复:

当前版本仅支持在ArkTS侧触发Native方法后回调ArkTS接口。

我觉得我的这种需求不算罕见吧,竟然不支持。再吐槽一句,官方目前这种调用方式也太鸡肋了,将 ArkTS 的方法传输到 C++ 侧调用… 主动调用 C++方法,再在 C++侧调用传入的 ArkTS方法。我实在想不到使用场景。

我也不等更新了,在线寻求一个能实现上面需求的方法。


更多关于HarmonyOS鸿蒙Next中如何在Native侧C++子线程直接调用ArkTS接口,不用通过ArkTS侧触发回调的实战教程也可以访问 https://www.itying.com/category-93-b0.html

7 回复

【背景知识】 许多涉及到跟设备交互的应用,都需要使用C++去跟设备交互,然后在C++中去创建子线程去接收设备返回来的信息,需要在C++子线程中调用ArkTS的方法,去把数据传到ArkTS中。

【解决方案】 在C++侧创建ArkTS线程安全函数:

struct CallbackObj {
    // arkts方法引用
    napi_ref cbObj;
    // 线程安全函数
    napi_threadsafe_function tsfn;
};

CallbackObj *callBack = new CallbackObj();

static napi_value NAPI_Global_setCallback(napi_env env, napi_callback_info info) {
  size_t argc = 1;
  napi_value args[1] = {nullptr};
  napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
  
  CallbackObj *downLoadCallBack = new CallbackObj();
  napi_value workName;
  // 指向napi_value js_cb 的 napi_ref cbObj,js_cb为获取到的arkts回调方法
  napi_create_reference(env, args[0], 1, &callBack->cbObj);
  // 通过UTF8编码的C字符串数据创建work_name
  napi_create_string_utf8(env, "DownLoadCallBack Work", NAPI_AUTO_LENGTH, &workName;);
  // 创建线程安全函数,存到tsfn中
  napi_create_threadsafe_function(env, args[0], NULL, workName, 0, 1, NULL, NULL, NULL, downLoadCallback, &callBack->tsfn);
  
  return nullptr;
}

void downLoadCallback(napi_env env, napi_value js_cb, void *context, void *data) {
  int connectState = *(int *)data;
  // 获取引用值
  OH_LOG_INFO(LOG_APP, "进入connectStateCallback");
  // 创建一个ArkTS number作为ArkTS function的入参。
  napi_value argv;
  napi_create_int32(env, connectState, &argv);
  
  // 调用回调函数
  int num;
  napi_value result = nullptr;
  napi_call_function(env, nullptr, js_cb, 1, &argv, &result);
  napi_get_value_int32(env, result, &num);
}

在C++的子线程中调用ArkTS方法:

在子线程中获取线程安全函数并执行:
napi_acquire_threadsafe_function(callBack->tsfn);
napi_call_threadsafe_function(callBack->tsfn, connectState, napi_tsfn_blocking);

在ArkTS中调用函数:

​import nativeFun from 'libentry.so';
@Entry
@Component
struct Index {
  @State message: string = '';
  ​
  build() {
    Row() {
      Column() {
        Text(this.message)
          .onClick(() => {
          nativeFun.nativeCallArkTS((a: number)=> {
            a++;
            this.message = a.toString()
            return a })
          })
        }
      .width('100%')
      }
    .height('100%')
  }
}

更多关于HarmonyOS鸿蒙Next中如何在Native侧C++子线程直接调用ArkTS接口,不用通过ArkTS侧触发回调的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


提供一段代码看看能不能帮助你

ArkTS侧注册回调

import webSocket from '@kit.NetworkKit';
import nativeBridge from 'libnative.so';

class WebSocketHandler {
  onMessageReceived = (data: string) => {
    console.log('收到消息:', data);
    // 更新UI显示逻辑
  }
  
  initWebSocket() {
    const ws = webSocket.createWebSocket();
    nativeBridge.registerMessageCallback(this.onMessageReceived);
    ws.connect('ws://example.com');
  }
}

Native侧桥接层

// 创建线程安全函数
napi_value RegisterCallback(napi_env env, napi_callback_info info) {
  napi_value jsCallback;
  napi_get_cb_info(env, info, nullptr, nullptr, &jsCallback, nullptr);
  
  auto ctx = new ThreadContext();
  napi_create_threadsafe_function(env, jsCallback, 
    nullptr, napi_tsfn_create_new, 
    0, 1, ctx, FinalizeCallback, &ctx->tsfn);
  
  napi_acquire_threadsafe_function(ctx->tsfn);
  return nullptr;
}

// WebSocket消息回调
void OnWebSocketMessage(void* data) {
  ThreadContext* ctx = static_cast<ThreadContext*>(data);
  const char* message = "从Native接收的数据";
  napi_call_threadsafe_function(ctx->tsfn, message, napi_tsfn_blocking);
}

WebSocket子线程中禁止直接操作ArkTS对象

通过napi_call_threadsafe_function实现异步队列化调用

二进制数据建议使用ArrayBuffer传输

// 传递二进制数据示例
napi_value buffer;
napi_create_arraybuffer(env, dataSize, &rawData, &buffer);
napi_call_threadsafe_function(ctx->tsfn, buffer, napi_tsfn_blocking);

楼主好,我来啦哈哈,加不加分不重要,主要我喜欢话痨。

Node-API提供的napi_create_threadsafe_function他这个允许在子线程中安全调用ArkTS函数。内部通过任务队列管理调用顺序,可以保证主线程按序执行回调。然后子线程通过napi_call_threadsafe_function投递任务,主线程异步处理。要注意napi_env仅在主线程有效,子线程需通过线程安全函数绕开直接操作napi_value的限制。这样无需ArkTS主动触发Native方法,通过模块初始化时注册回调实现主动调用。

废话不多直接上步骤:

ArkTS侧注册回调函数

// index.ets

import testNapi from 'libentry.so';

@Entry

@Component

struct Index {

  @State message: string = "Hello World";

  onPageShow() {

    // 注册回调函数供Native调用

    testNapi.registerCallback((msg: string) => {

      this.message = msg; // 更新UI

    });

  }

  build() { /* UI布局 */ }

}

Native侧创建线程安全函数

// 定义线程安全函数结构体

struct ThreadSafeInfo {

  napi_threadsafe_function tsfn;

  std::string message;

};

// 创建线程安全函数

napi_value RegisterCallback(napi_env env, napi_callback_info info) {

  //  获取ArkTS传递的回调函数

  size_t argc = 1;

  napi_value args;

  napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

  // 创建线程安全函数

  napi_threadsafe_function tsfn;

  napi_create_threadsafe_function(

    env, args, nullptr, nullptr,

    0, 1, nullptr, nullptr, nullptr,

    [](napi_env env, napi_value js_cb, void* context, void* data) {

      // 主线程执行回调

      ThreadSafeInfo* info = static_cast<ThreadSafeInfo*>(data);

      napi_value argv;

      napi_create_string_utf8(env, info->message.c_str(), info->message.size(), &argv);

      napi_call_function(env, nullptr, js_cb, 1, &argv, nullptr);

      delete info;

    }, &tsfn

  );

  // 存储tsfn供子线程使用

  ThreadSafeInfo* threadInfo = new ThreadSafeInfo{tsfn};

  napi_wrap(env, args, threadInfo, [](napi_env, void* data, void*) {

    napi_release_threadsafe_function(static_cast<ThreadSafeInfo*>(data)->tsfn, napi_tsfn_release);

    delete static_cast<ThreadSafeInfo*>(data);

  }, nullptr, nullptr);

  return nullptr;

}

子线程触发回调

// WebSocket消息接收线程

void OnMessageCallback(const char* msg) {

  // 构造消息数据

  ThreadSafeInfo* info = new ThreadSafeInfo;

  info->message = std::string(msg);

  // 向主线程投递任务

  napi_call_threadsafe_function(info->tsfn, info, napi_tsfn_blocking);

}

很详细,等我明天试试,

180 都换不来你们心动吗,虽然我也不知道这积分有啥用,但是这个量已经能看出我的心切了吧[doge]

在HarmonyOS鸿蒙Next中,Native侧C++子线程无法直接调用ArkTS接口。ArkTS接口调用必须在主线程执行。可通过Native侧向ArkTS侧发送消息,由ArkTS侧在主线程处理调用。使用NAPI的异步工作线程机制,通过napi_create_async_work创建异步任务,在complete回调中触发ArkTS侧处理。需确保线程安全,避免直接跨线程访问。

目前HarmonyOS Next的Native API设计确实限制了从C++子线程直接调用ArkTS方法的能力。针对WebSocket OnMessage场景,建议采用以下替代方案:

  1. 使用消息队列机制:在Native侧维护一个线程安全的队列,将收到的消息存入队列。ArkTS侧通过定时轮询或事件监听方式主动拉取消息。

  2. 通过Native侧触发事件:利用已有的ArkTS到Native的回调机制,在Native侧保存ArkTS传递的回调方法。当OnMessage触发时,通过工作线程将消息投递到主线程,再调用预先保存的ArkTS回调。

  3. 使用共享内存+信号量:在Native和ArkTS间建立共享内存区域,通过信号量机制实现跨线程通信,避免直接方法调用。

这些方案虽然需要额外封装,但能有效解决当前版本的限制。建议关注后续SDK更新,官方可能会提供更直接的原生支持。

回到顶部