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