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```