首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >HarmonyOS 开发实践 —— C++子线程与UI主线程通信方案

HarmonyOS 开发实践 —— C++子线程与UI主线程通信方案

原创
作者头像
小帅聊鸿蒙
发布2024-12-20 14:18:06
发布2024-12-20 14:18:06
31900
代码可运行
举报
文章被收录于专栏:鸿蒙开发笔记鸿蒙开发笔记
运行总次数:0
代码可运行

异步流程介绍

ArkTS是单线程的,其通过事件循环机制来处理异步任务,这一特性,同样也深入影响了Node-API的模块开发。在Native线程中既要保证没有多线程问题,又要保证任务的正确执行。

基于threadsafe的实现

方案概述

针对在HarmonyOS中,Native子线程不能直接访问ArkTS主线程的资源的场景,Node-API提供了napi_threadsafe_function这一套线程安全函数接口,用来实现异步通信操作。下面介绍napi_threadsafe_function的方式。

实现流程

应用侧传入需要执行的异步任务。

Native侧通过napi_create_threadsafe_function创建线程安全函数,将callback注册到线程安全函数中,等待后续其他线程抛异步任务进行回调。

通过napi_create_async_work,将ExecuteWork与WorkComplete添加为异步任务的执行回调与完成回调。

通过ExecuteWork函数进行业务处理,并执行线程安全函数,通过WorkComplete函数进行任务执行完成后的资源清理回收工作。

通过注册给线程安全函数的callback回到主线程,然后通过napi_call_function调用ArkTS函数回到ArkTS侧。

在ArkTS线程中解析或拒绝promise的唯一方法。

代码实现

C++:

  1. Native侧通过napi_create_threadsafe_function创建线程安全函数,将callback注册到线程安全函数中,等待后续其他线程抛异步任务进行回调。
代码语言:cpp
代码运行次数:0
运行
复制
struct CallbackData {
  napi_threadsafe_function tsfn;
  napi_async_work work;
};
 
static napi_value StartThread(napi_env env, napi_callback_info info)
{
  size_t argc = 1;
  napi_value jsCb = nullptr;
  CallbackData *callbackData = nullptr;
  napi_get_cb_info(env, info, &argc, &jsCb, nullptr, reinterpret_cast<void **>(&callbackData));
 
  // 创建一个线程安全函数
  napi_value resourceName = nullptr;
  napi_create_string_utf8(env, "Thread-safe Function Demo", NAPI_AUTO_LENGTH, &resourceName);
  napi_create_threadsafe_function(env, jsCb, nullptr, resourceName, 0, 1, callbackData, nullptr,
    callbackData, CallJs, &callbackData->tsfn);
 
  // 创建一个异步任务
  napi_create_async_work(env, nullptr, resourceName, ExecuteWork, WorkComplete, callbackData,
    &callbackData->work);
 
  // 将异步任务加入到异步队列中
  napi_queue_async_work(env, callbackData->work);
  return nullptr;
}
  1. 通过ExecuteWork函数进行业务处理,并执行线程安全函数,通过WorkComplete函数进行任务执行完成后的资源清理回收工作。
代码语言:cpp
代码运行次数:0
运行
复制
static void ExecuteWork(napi_env env, void *data) {
  CallbackData_ThreadSafe *callbackData = reinterpret_cast<CallbackData_ThreadSafe *>(data);
  std::promise<std::string> promise;
  auto future = promise.get_future();
  napi_call_threadsafe_function(callbackData->tsfn, &promise, napi_tsfn_nonblocking);
  try {
    auto result = future.get();
    OH_LOG_INFO(LOG_APP, "XXX, Result from JS %{public}s", result.c_str());
  } catch (const std::exception &e) {
    OH_LOG_INFO(LOG_APP, "XXX, Result from JS %{public}s", e.what());
  }
}
 
static void WorkComplete(napi_env env, napi_status status, void *data) {
  CallbackData_ThreadSafe *callbackData = reinterpret_cast<CallbackData_ThreadSafe *>(data);
  napi_release_threadsafe_function(callbackData->tsfn, napi_tsfn_release);
  napi_delete_async_work(env, callbackData->work);
  callbackData->tsfn = nullptr;
  callbackData->work = nullptr;
}
  1. 通过注册给线程安全函数的callback回到主线程,然后通过napi_call_function调用ArkTS 函数回到ArkTS侧。
代码语言:ts
复制
static void CallJs(napi_env env, napi_value jsCb, void *context, void *data) {
  if (env == nullptr) {
    return;
  }
  napi_value undefined = nullptr;
  napi_value promise = nullptr;
  napi_get_undefined(env, &undefined);
  napi_call_function(env, undefined, jsCb, 0, nullptr, &promise);
  napi_value thenFunc = nullptr;
  if (napi_get_named_property(env, promise, "then", &thenFunc) != napi_ok) {
    return;
  }
  napi_value resolvedCallback;
  napi_value rejectedCallback;
  napi_create_function(env, "resolvedCallback", NAPI_AUTO_LENGTH, ResolvedCallback, data, &resolvedCallback);
  napi_create_function(env, "rejectedCallback", NAPI_AUTO_LENGTH, RejectedCallback, data, &rejectedCallback);
  napi_value argv[2] = {resolvedCallback, rejectedCallback};
  napi_call_function(env, promise, thenFunc, 2, argv, nullptr);
}
  1. 在ArkTS线程中解析或拒绝promise。
代码语言:cpp
代码运行次数:0
运行
复制
static napi_value ResolvedCallback(napi_env env, napi_callback_info info) {
  void *data = nullptr;
  size_t argc = 1;
  napi_value argv[1];
  if (napi_get_cb_info(env, info, &argc, argv, nullptr, &data) != napi_ok) {
    return nullptr;
  }
  size_t result = 0;
  char buf[32] = {0};
  napi_get_value_string_utf8(env, argv[0], buf, 32, &result);
  reinterpret_cast<std::promise<std::string> *>(data)->set_value(std::string(buf));
  return nullptr;
}
 
static napi_value RejectedCallback(napi_env env, napi_callback_info info) {
  void *data = nullptr;
  if (napi_get_cb_info(env, info, nullptr, nullptr, nullptr, &data) != napi_ok) {
    return nullptr;
  }
  reinterpret_cast<std::promise<std::string> *>(data)->set_exception(
    std::make_exception_ptr(std::runtime_error("Error in jsCallback")));
  return nullptr;
}

TS:

代码语言:ts
复制
import nativeModule from 'libentry.so';
 
// 应用侧传入需要执行的异步任务
let callback = (): Promise<string> => {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve("string from promise");
    }, 5000);
  });
}
nativeModule.startThread(callback);

写在最后

如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:

  • 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力;
  • 关注小编,同时可以期待后续文章ing🚀,不定期分享原创知识;
  • 想要获取更多完整鸿蒙最新学习知识点,可关注B站:码牛课堂;

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 异步流程介绍
  • 基于threadsafe的实现
    • 方案概述
    • 实现流程
    • 代码实现
  • 写在最后
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档