• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 使用JSVM-API实现JS与C/C++语言交互开发流程
2
3使用JSVM-API实现跨语言交互,首先需要按照JSVM-API的机制实现模块的注册和加载等相关动作。
4
5- ArkTS/JS侧:实现C++方法的调用。代码比较简单,import一个对应的so库后,即可调用C++方法。
6
7- Native侧:.cpp文件,实现模块的注册。需要提供注册lib库的名称,并在注册回调方法中定义接口的映射关系,即Native方法及对应的JS/ArkTS接口名称等。
8
9此处以在ArkTS/JS侧实现RunJsVm()接口、在Native侧实现RunJsVm()接口,从而实现跨语言交互为例,呈现使用JSVM-API进行跨语言交互的流程。
10
11## 创建Native C++工程
12
13具体见[创建NDK工程](create-with-ndk.md)
14
15## Native侧方法的实现
16
17参考[使用Node-API实现跨语言交互开发流程](use-napi-process.md#native侧方法的实现),以下代码提供了“使用JSVM-API实现JS与C/C++语言交互开发流程”Native侧方法实现的一个demo。
18
19- 在index.d.ts文件中,提供JS侧的接口方法。
20
21  ```ts
22  // entry/src/main/cpp/types/libentry/index.d.ts
23  export const runTest: () => void;
24  ```
25
26- 在oh-package.json5文件中将index.d.ts与cpp文件关联起来。
27
28  ```json
29  {
30    "name": "libentry.so",
31    "types": "./index.d.ts",
32    "version": "",
33    "description": "Please describe the basic information."
34  }
35  ```
36
37- 在CMakeLists.txt文件中配置CMake打包参数。
38
39  ```text
40  # entry/src/main/cpp/CMakeLists.txt
41  cmake_minimum_required(VERSION 3.4.1)
42  project(JSVMDemo)
43
44  set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
45  # 日志打印配置
46  add_definitions( "-DLOG_DOMAIN=0xd0d0" )
47  add_definitions( "-DLOG_TAG=\"testTag\"" )
48  include_directories(${NATIVERENDER_ROOT_PATH}
49                      ${NATIVERENDER_ROOT_PATH}/include)
50
51  # 添加名为entry的库
52  add_library(entry SHARED hello.cpp)
53  # 构建此可执行文件需要链接的库
54  target_link_libraries(entry PUBLIC libace_napi.z.so libjsvm.so libhilog_ndk.z.so)
55  ```
56
57- 实现Native侧的runTest接口。具体代码如下:
58
59  ```cpp
60  // entry/src/main/cpp/hello.cpp
61  #include "napi/native_api.h"
62  #include "hilog/log.h"
63  #include "ark_runtime/jsvm.h"
64
65  #define LOG_DOMAIN 0x3200
66  #define LOG_TAG "APP"
67
68  static int g_aa = 0;
69
70  #define CHECK_RET(theCall)                                                                                             \
71      do {                                                                                                               \
72          JSVM_Status cond = theCall;                                                                                    \
73          if ((cond) != JSVM_OK) {                                                                                       \
74              const JSVM_ExtendedErrorInfo *info;                                                                        \
75              OH_JSVM_GetLastErrorInfo(env, &info);                                                                      \
76              OH_LOG_ERROR(LOG_APP, "jsvm fail file: %{public}s line: %{public}d ret = %{public}d message = %{public}s", \
77                           __FILE__, __LINE__, cond, info != nullptr ? info->errorMessage : "");                         \
78              return -1;                                                                                                 \
79          }                                                                                                              \
80      } while (0)
81
82  #define CHECK(theCall)                                                                                                 \
83      do {                                                                                                               \
84          JSVM_Status cond = theCall;                                                                                    \
85          if ((cond) != JSVM_OK) {                                                                                       \
86              OH_LOG_ERROR(LOG_APP, "jsvm fail file: %{public}s line: %{public}d ret = %{public}d", __FILE__, __LINE__,  \
87                           cond);                                                                                        \
88              return -1;                                                                                                 \
89          }                                                                                                              \
90      } while (0)
91
92  // 用于调用theCall并检查其返回值是否为JSVM_OK。
93  // 如果不是,则调用OH_JSVM_GetLastErrorInfo处理错误并返回retVal。
94  #define JSVM_CALL_BASE(env, theCall, retVal)                                                                           \
95      do {                                                                                                               \
96          JSVM_Status cond = theCall;                                                                                    \
97          if (cond != JSVM_OK) {                                                                                         \
98              const JSVM_ExtendedErrorInfo *info;                                                                        \
99              OH_JSVM_GetLastErrorInfo(env, &info);                                                                      \
100              OH_LOG_ERROR(LOG_APP, "jsvm fail file: %{public}s line: %{public}d ret = %{public}d message = %{public}s", \
101                           __FILE__, __LINE__, cond, info != nullptr ? info->errorMessage : "");                         \
102              return retVal;                                                                                             \
103          }                                                                                                              \
104      } while (0)
105
106  // JSVM_CALL_BASE的简化版本,返回nullptr
107  #define JSVM_CALL(theCall) JSVM_CALL_BASE(env, theCall, nullptr)
108
109  // OH_JSVM_StrictEquals的样例方法
110  static JSVM_Value IsStrictEquals(JSVM_Env env, JSVM_CallbackInfo info) {
111      // 接受两个入参
112      size_t argc = 2;
113      JSVM_Value args[2] = {nullptr};
114      JSVM_CALL(OH_JSVM_GetCbInfo(env, info, &argc, args, nullptr, nullptr));
115      // 调用OH_JSVM_StrictEquals接口判断给定的两个JavaScript value是否严格相等
116      bool result = false;
117      JSVM_Status status = OH_JSVM_StrictEquals(env, args[0], args[1], &result);
118      if (status != JSVM_OK) {
119          OH_LOG_ERROR(LOG_APP, "JSVM OH_JSVM_StrictEquals: failed");
120      } else {
121          OH_LOG_INFO(LOG_APP, "JSVM OH_JSVM_StrictEquals: success: %{public}d", result);
122      }
123      JSVM_Value isStrictEqual;
124      JSVM_CALL(OH_JSVM_GetBoolean(env, result, &isStrictEqual));
125      return isStrictEqual;
126  }
127  // IsStrictEquals注册回调
128  static JSVM_CallbackStruct param[] = {
129      {.data = nullptr, .callback = IsStrictEquals},
130  };
131  static JSVM_CallbackStruct *method = param;
132  // IsStrictEquals方法别名,供JS调用
133  static JSVM_PropertyDescriptor descriptor[] = {
134      {"isStrictEquals", nullptr, method++, nullptr, nullptr, nullptr, JSVM_DEFAULT},
135  };
136  // 样例测试js
137  const char *srcCallNative = R"JS(    let data = '123';
138      let value = 123;
139      isStrictEquals(data,value);)JS";
140
141  static int32_t TestJSVM() {
142      JSVM_InitOptions initOptions = {0};
143      JSVM_VM vm;
144      JSVM_Env env = nullptr;
145      JSVM_VMScope vmScope;
146      JSVM_EnvScope envScope;
147      JSVM_HandleScope handleScope;
148      JSVM_Value result;
149      // 初始化JavaScript引擎实例
150      if (g_aa == 0) {
151          g_aa++;
152         CHECK(OH_JSVM_Init(&initOptions));
153      }
154      // 创建JSVM环境
155      CHECK(OH_JSVM_CreateVM(nullptr, &vm));
156      CHECK(OH_JSVM_CreateEnv(vm, sizeof(descriptor) / sizeof(descriptor[0]), descriptor, &env));
157      CHECK(OH_JSVM_OpenVMScope(vm, &vmScope));
158      CHECK_RET(OH_JSVM_OpenEnvScope(env, &envScope));
159      CHECK_RET(OH_JSVM_OpenHandleScope(env, &handleScope));
160
161      // 通过script调用测试函数
162      JSVM_Script script;
163      JSVM_Value jsSrc;
164      CHECK_RET(OH_JSVM_CreateStringUtf8(env, srcCallNative, JSVM_AUTO_LENGTH, &jsSrc));
165      CHECK_RET(OH_JSVM_CompileScript(env, jsSrc, nullptr, 0, true, nullptr, &script));
166      CHECK_RET(OH_JSVM_RunScript(env, script, &result));
167
168      // 销毁JSVM环境
169      CHECK_RET(OH_JSVM_CloseHandleScope(env, handleScope));
170      CHECK_RET(OH_JSVM_CloseEnvScope(env, envScope));
171      CHECK(OH_JSVM_CloseVMScope(vm, vmScope));
172      CHECK(OH_JSVM_DestroyEnv(env));
173      CHECK(OH_JSVM_DestroyVM(vm));
174      return 0;
175  }
176
177  static napi_value RunTest(napi_env env, napi_callback_info info)
178  {
179      TestJSVM();
180      return nullptr;
181  }
182
183  // 模块初始化
184  EXTERN_C_START
185  static napi_value Init(napi_env env, napi_value exports) {
186      // 实现ArkTS接口与C++接口的绑定和映射
187      napi_property_descriptor desc[] = {
188        {"runTest", nullptr, RunTest, nullptr, nullptr, nullptr, napi_default, nullptr}
189      };
190      // 在exports对象上挂载RunJsVm的Native方法
191      napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
192      return exports;
193  }
194  EXTERN_C_END
195
196  static napi_module demoModule = {
197      .nm_version = 1,
198      .nm_flags = 0,
199      .nm_filename = nullptr,
200      .nm_register_func = Init,
201      .nm_modname = "entry",
202      .nm_priv = ((void *)0),
203      .reserved = {0},
204  };
205
206  extern "C" __attribute__((constructor)) void RegisterEntryModule(void) { napi_module_register(&demoModule); }
207  ```
208
209## ArkTS侧调用C/C++方法实现
210
211```ts
212import hilog from '@ohos.hilog'
213// 通过import的方式,引入Native能力。
214import napitest from 'libentry.so'
215
216@Entry
217@Component
218struct Index {
219  @State message: string = 'Hello World';
220
221  build() {
222    Row() {
223      Column() {
224        Text(this.message)
225          .fontSize(50)
226          .fontWeight(FontWeight.Bold)
227          .onClick(() => {
228            // runtest
229            napitest.runTest();
230          })
231      }
232      .width('100%')
233    }
234    .height('100%')
235  }
236}
237```
238
239预期输出结果
240```ts
241JSVM OH_JSVM_StrictEquals: success: 0
242```