场景描述

app应用在native侧调用 系统库/arkts模块的方法。

应用经常会遇到如下的业务诉求:

场景一:系统提供了ArkTS 接口,但未提供对应的NDK接口,当伙伴使用C++ 代码实现业务逻辑时,部分系统能力需要依赖系统ArkTS接口;

场景二: 系统仅提供了ArkTS 异步接口,未提供对应的NDK接口,当伙伴使用C++ 代码实现业务逻辑时,部分系统能力需要依赖系统ArkTS 异步接口;

场景三:伙伴在 TS 侧已定义接口,未实现对应的NDK接口,当伙伴使用C++ 代码实现业务逻辑时,想直接使用已有的TS 接口;

方案描述

场景一

系统提供了ArkTS 接口,但未提供对应的NDK接口,当伙伴使用C++ 代码实现业务逻辑时,部分系统能力需要依赖系统ArkTS接口;

例如: 获取设备的屏幕宽高。

方案

通过napi_load_module 的方式调用系统模块接口。

image.png

核心代码

static napi_value GetDisplaySize(napi_env env, napi_callback_info info) { 
  // 获取arkts侧的系统库路径 
  char path[64] = "@ohos.display"; 
  size_t typeLen = 0; 
  napi_value string; 
  napi_create_string_utf8(env, path,  typeLen, &string); 
  // 加载系统库 
  napi_value sysModule; 
  napi_load_module(env, path, &sysModule); 
  // 获取系统库中的"getDefaultDisplaySync"方法 
  napi_value func = nullptr; 
  napi_get_named_property(env, sysModule, "getDefaultDisplaySync", &func); 
 
  napi_value funcResult; 
  napi_call_function(env, sysModule, func, 0, nullptr, &funcResult); 
 
  napi_value widthValue = nullptr; 
  napi_get_named_property(env, funcResult, "width", &widthValue); 
 
  double width; 
  napi_get_value_double(env, widthValue, &width); 
  OH_LOG_INFO( LOG_APP,  "width: %{public}f", width); 
 
  napi_value heightValue = nullptr; 
 
  napi_get_named_property(env, funcResult, "height", &heightValue); 
 
  double height; 
  napi_get_value_double(env, heightValue, &height); 
  OH_LOG_INFO(LOG_APP, "height: %{public}f", height); 
 
  // TODO: 业务拿到width 和 height,可以进一步处理具体业务逻辑 
 
  return nullptr; 
}

运行结果

image.png

场景二

系统仅提供了ArkTS 异步接口,未提供对应的NDK接口,当伙伴使用C++ 代码实现业务逻辑时,部分系统能力需要依赖系统ArkTS 异步接口;

例如: 如何访问系统定义的异步ArkTS方法。

方案

通过创建线程安全函数的方式 调用系统的异步接口

image.png

核心代码

回调到JS层

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; 
  char str[] = "wlan0"; 
 
  napi_value sysModule; 
  napi_load_module(env, "@ohos.net.statistics", &sysModule); 
 
  napi_value param; 
  napi_create_string_utf8(env, str, NAPI_AUTO_LENGTH, &param); 
  napi_call_function(env, sysModule, jsCb, 1, &param, &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); 
} 
// 执行异步任务 
static void ExecuteWork(napi_env env, void *data) { 
  CallbackData *callbackData = reinterpret_cast<CallbackData *>(data); 
  std::promise<double> 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, "getIfaceRxBytes Result from JS %{public}f", result); 
  } 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 *callbackData = reinterpret_cast<CallbackData *>(data); 
  napi_release_threadsafe_function(callbackData->tsfn, napi_tsfn_release); 
  napi_delete_async_work(env, callbackData->work); 
  callbackData->tsfn = nullptr; 
  callbackData->work = nullptr; 
} 
static napi_value CallAsyncFunc(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 sysModule; 
  napi_load_module(env, "@ohos.net.statistics", &sysModule); 
 
  napi_value getIfaceRxBytesFunc ; 
  napi_get_named_property(env, sysModule, "getIfaceRxBytes", &getIfaceRxBytesFunc); 
 
  // 创建一个线程安全函数 
  napi_value resourceName = nullptr; 
  napi_create_string_utf8(env, "CallAsyncFunc", NAPI_AUTO_LENGTH, &resourceName); 
  napi_create_threadsafe_function(env, getIfaceRxBytesFunc, 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; 
}

运行结果

image.png
 

场景三

伙伴在 ArkTS/TS 侧已定义接口,当伙伴使用C++ 代码实现业务逻辑时,想直接使用已有的TS 接口;

例如: 如何调用自定义的 ArkTS/TS方法;

方案

image.png

核心代码

步骤一: ObjectUtil.ts 导出相关接口

namespace ObjectUtil { 
  export function isNull(obj) { 
    return obj === null; 
  } 
  export function isUndefined(obj) { 
    return obj === undefined; 
  } 
  export function isNullOrUndefined(obj) { 
    return isNull(obj) || isUndefined(obj); 
  } 
  export function toString(obj, defaultValue = '') { 
    if (this.isNullOrUndefined(obj)) { 
      return defaultValue; 
    } 
    else { 
      return obj.toString(); 
    } 
  } 
} 
export default ObjectUtil;

步骤二:必须在 build-profile.json5添加ets文件配置

// 必须在build-profile.json5添加配置 
"arkOptions": { 
  "runtimeOnly": { 
    "sources": [ 
    './src/main/ets/common/ObjectUtil.ts', 
    ], 
    "packages": [ 
    ] 
  } 
},

步骤三: 在native 获取ets 模块导出的变量

/* 
* 获取某个TS/JS模块导出变量 
* 入参: 
* path - 在工程文件夹下从ets开始的绝对路径名 
* key - 待加载TS/JS模块导出变量的属性名 
* 返回值:获取的TS/JS模块导出变量 
*/ 
static napi_value GetNativeModule(napi_env env, const char *modulePath, const char *key) 
{ 
  napi_value module; 
  // 通过modulePath获取对应TS/JS模块的导出对象 
  napi_load_module(env, modulePath, &module); 
 
  napi_value outputObject = nullptr; 
  // 通过对象属性名key,从导出模块变量 
  napi_get_named_property(env, module, key, &outputObject ); 
  return outputObject ; 
}

步骤四:获取方法并调用demo示例(获取/ets/common/ObjectUtil 的isNull方法,并调用isNull方法判断一个对象是否为null)

/* 
* 调用系统库/ets/common/ObjectUtil模块导出方法 /ets/common/ObjectUtil 的isNull方法 
* 返回值:标志是否加载成功的布尔值 
*/ 
static napi_status CallIsNullFun(napi_env env, napi_value inputObject) 
{ 
  const char moduleName[] = "/ets/common/ObjectUtil"; 
  const char funcName[] = "isNull"; 
  napi_value isNullFun = GetNativeModule(env, moduleName, funcName); 
  ​ 
  napi_value inputArgs[1] = inputObject; 
  napi_value result; 
  // infoFun为"/ets/common/ObjectUtil"模块中获取的函数方法 
  // inputArgs为待执行方法的入参,result为出参 
  napi_call_function(env, undefined, isNullFun , 1, inputArgs, result); 
  ​ 
  return result ; 
}

HarmonyOS码上奇行
5k 声望2.5k 粉丝