鸿蒙5.0实战案例:基于JSVM创建引擎执行JS代码并烧毁

打印 上一主题 下一主题

主题 1784|帖子 1784|积分 5352

往期推文全新看点(文中附带全新鸿蒙5.0全栈学习笔录)

✏️ 鸿蒙应用开发与鸿蒙体系开发哪个更有前景?

✏️ 嵌入式开发适不适合做鸿蒙南向开发?看完这篇你就了解了~

✏️ 对于大前端开发来说,转鸿蒙开发毕竟是福还是祸?

✏️ 鸿蒙岗位需求突增!移动端、PC端、IoT到底该怎么选?

✏️ 记录一场鸿蒙开发岗位面试经历~

✏️ 连续更新中……


场景描述

通过JSVM,可以在应用运行期间直接执行一段动态加载的JS代码。也可以选择将一些对性能、底层体系调用有较高要求的焦点功能用C/C++实现并将C++方法注册到JS侧,在JS代码中直接调用,提高应用的执行服从。
功能描述

通过createJsCore方法来创建一个新的JS基础运行时环境,并通过该方法得到一个虚拟机ID,通过evalUateJS方法利用虚拟机ID对应的运行环境来运行JS代码,在JS代码中创建promise并异步调取TS侧设定的callback函数,最后利用releaseJsCore方法来开释虚拟机ID对应的运行环境。
JSVM 主要交互流程


接口介绍

创建所需接口:

  1. // 初始化虚拟机
  2. JSVM_EXTERN JSVM_Status OH_JSVM_Init(const JSVM_InitOptions* options);
  3. // 创建虚拟机实例
  4. JSVM_EXTERN JSVM_Status OH_JSVM_CreateVM(const JSVM_CreateVMOptions* options,JSVM_VM* result);
  5. // 创建虚拟机作用域
  6. JSVM_EXTERN JSVM_Status OH_JSVM_OpenVMScope(JSVM_VM vm,JSVM_VMScope* result);
  7. // 创建上下文环境
  8. JSVM_EXTERN JSVM_Status OH_JSVM_CreateEnv(JSVM_VM vm,size_t propertyCount,const JSVM_PropertyDescriptor* properties,JSVM_Env* result);
  9. // 创建上下文环境作用域
  10. JSVM_EXTERN JSVM_Status OH_JSVM_OpenEnvScope(JSVM_Env env,JSVM_EnvScope* result);
复制代码
执行所需接口:

  1. // 开启新作用域
  2. JSVM_EXTERN JSVM_Status OH_JSVM_OpenHandleScope(JSVM_Env env,JSVM_HandleScope* result);
  3. // 编译一串JS代码,并返回编译后脚本
  4. JSVM_EXTERN JSVM_Status OH_JSVM_CompileScript(JSVM_Env env,JSVM_Value script,const uint8_t* cachedData,size_t cacheDataLength,bool eagerCompile,bool* cacheRejected,JSVM_Script* result);
  5. // 执行一段JS代码并返回结果
  6. JSVM_EXTERN JSVM_Status OH_JSVM_RunScript(JSVM_Env env,JSVM_Script script,JSVM_Value* result);
  7. // 关闭传入的作用域
  8. JSVM_EXTERN JSVM_Status OH_JSVM_CloseHandleScope(JSVM_Env env,JSVM_HandleScope scope);
复制代码
关闭所需接口:

  1. // 关闭上下文环境作用域
  2. JSVM_EXTERN JSVM_Status OH_JSVM_CloseEnvScope(JSVM_Env env,JSVM_EnvScope scope);
  3. // 销毁上下文环境
  4. JSVM_EXTERN JSVM_Status OH_JSVM_DestroyEnv(JSVM_Env env);
  5. // 关闭虚拟机作用域
  6. JSVM_EXTERN JSVM_Status OH_JSVM_CloseVMScope(JSVM_VM vm,JSVM_VMScope scope);
  7. // 销毁虚拟机实例
  8. JSVM_EXTERN JSVM_Status OH_JSVM_DestroyVM(JSVM_VM vm);
复制代码
场景代码

场景一: JSVM 执行JS代码并回调C++代码

创建虚拟机所需环境,编译并执行JS代码回调C++代码,烧毁之前创建的虚拟机环境。
配置
cmakelists:
  1. target_link_libraries(entry PUBLIC libjsvm.so)
复制代码
ArkTS代码:
示例中包含了提供给JSVM的JS里的consoleinfo同步方法,createPromise创建异步方法,执行assertEqual方法,onJSResultCallback异步方法回调。

  • 调用Native的创建虚拟机及环境的接口,绑定MyCallback回调(用于JS代码里onJSResultCallback方法的回调),吸收native返回的虚拟机id。
  • 调用Native的执行JS代码的接口,传入虚拟机id和JS代码字符串,在native侧的虚拟机里编译并执行。
  • 调用Native的烧毁虚拟机及环境的接口,传入虚拟机id,指定烧毁对应的虚拟机和环境。
可以同时运行多个JSVM虚拟机执行多个JS代码。
  1. import testNapi from 'libentry.so';
  2. // 用于JSVM执行的JS代码字符串
  3. let jsCodeStr = `{
  4. let a = "hello World";
  5. consoleinfo(a);
  6. let b = add(99, 1);
  7. const mPromise = createPromise();
  8. mPromise.then((result) => {
  9. assertEqual(result, 0);
  10. onJSResultCallback(result, "abc", "v");
  11. });
  12. a;
  13. };`;
  14. function MyCallback(a:string, b:string):string {
  15.   console.log("TEST MyCallback run: " + a);
  16.   b = "callback done";
  17.   console.log("TEST MyCallback run: " + b);
  18.   return "callback pass";
  19. }
  20. // 创建首个运行环境,并绑定TS回调
  21. const coreId = testNapi.createJsCore(MyCallback);
  22. // 在首个运行环境中执行JS代码
  23. testNapi.evalUateJS(coreId, jsCodeStr);
  24. // 释放首个运行环境
  25. testNapi.releaseJsCore(coreId);
复制代码
Native代码:
JS运行环境创建,创建让JS代码运行的虚拟机环境:

  • 通过OH_JSVM_Init初始化虚拟机。
  • 通过OH_JSVM_CreateVM创建虚拟机实例。
  • 通过OH_JSVM_OpenVMScope创建虚拟机作用域。
  • 将本地函数的回调函数放到JSVM_PropertyDescriptor集合中(用于JS调用的C++函数)。
  • 通过OH_JSVM_CreateEnv创建上下文环境并注册JSVM_PropertyDescriptor。
  • 通过OH_JSVM_OpenEnvScope创建上下文环境作用域。
  1. static void CreateArkJSContext() {
  2.   JSVM_Status status;
  3.   JSVM_InitOptions init_options;
  4.   memset(&init_options, 0, sizeof(init_options));
  5.   if (aa == 0) {
  6.  
  7.     //****1****初始化JSVM
  8.     OH_JSVM_Init(&init_options);
  9.     aa++;
  10.   }
  11.  
  12.   g_vmMap[ENVTAG_NUMBER] = new JSVM_VM;
  13.   JSVM_VMScope vmScope;
  14.   g_vmScopeMap[ENVTAG_NUMBER] = vmScope;
  15.   JSVM_CreateVMOptions options;
  16.   memset(&options, 0, sizeof(options));
  17.  
  18.   //****2****创建JSVM实例
  19.   status = OH_JSVM_CreateVM(&options, g_vmMap[ENVTAG_NUMBER]);
  20.  
  21.   //****3****创建JSVM作用域
  22.   status = OH_JSVM_OpenVMScope(*g_vmMap[ENVTAG_NUMBER], &g_vmScopeMap[ENVTAG_NUMBER]);
  23.  
  24.   g_envMap[ENVTAG_NUMBER] = new JSVM_Env;
  25.   g_callBackStructMap[ENVTAG_NUMBER] = new JSVM_CallbackStruct[5];
  26.   // 注册用户提供的本地函数的回调函数指针和数据,通过JSVM-API暴露给JS
  27.   for (int i = 0; i < 5; i++) {
  28.     g_callBackStructMap[ENVTAG_NUMBER][i].data = nullptr;
  29.   }
  30.   g_callBackStructMap[ENVTAG_NUMBER][0].callback = Consoleinfo;
  31.   g_callBackStructMap[ENVTAG_NUMBER][1].callback = Add;
  32.   g_callBackStructMap[ENVTAG_NUMBER][2].callback = AssertEqual;
  33.   g_callBackStructMap[ENVTAG_NUMBER][3].callback = OnJSResultCallback;
  34.   g_callBackStructMap[ENVTAG_NUMBER][4].callback = CreatePromise;
  35.  
  36.   //****4****将本地函数的回调函数放到JSVM_PropertyDescriptor集合中
  37.   JSVM_PropertyDescriptor descriptors[] = {
  38.     {"consoleinfo", NULL, &g_callBackStructMap[ENVTAG_NUMBER][0], NULL, NULL, NULL, JSVM_DEFAULT},
  39.     {"add", NULL, &g_callBackStructMap[ENVTAG_NUMBER][1], NULL, NULL, NULL, JSVM_DEFAULT},
  40.     {"assertEqual", NULL, &g_callBackStructMap[ENVTAG_NUMBER][2], NULL, NULL, NULL, JSVM_DEFAULT},
  41.     {"onJSResultCallback", NULL, &g_callBackStructMap[ENVTAG_NUMBER][3], NULL, NULL, NULL, JSVM_DEFAULT},
  42.     {"createPromise", NULL, &g_callBackStructMap[ENVTAG_NUMBER][4], NULL, NULL, NULL, JSVM_DEFAULT},
  43.   };
  44. //****5****创建JSVM环境
  45. status = OH_JSVM_CreateEnv(*g_vmMap[ENVTAG_NUMBER], sizeof(descriptors) / sizeof(descriptors[0]), descriptors, g_envMap[ENVTAG_NUMBER]);
  46. JSVM_EnvScope envScope;
  47. g_envScopeMap[ENVTAG_NUMBER] = envScope;
  48. //****6****创建JSVM环境作用域
  49. status = OH_JSVM_OpenEnvScope(*g_envMap[ENVTAG_NUMBER], &g_envScopeMap[ENVTAG_NUMBER]);
  50. }
  51. // 提供创建JSVM运行环境的对外接口并返回对应唯一ID
  52. static napi_value CreateJsCore(napi_env env1, napi_callback_info info) {
  53.   OH_LOG_ERROR(LOG_APP, "JSVM CreateJsCore START");
  54.   size_t argc = 1;
  55.   napi_value argv[1];
  56.   napi_get_cb_info(env1, info, &argc, argv, nullptr, nullptr);
  57.   if (argc < 1) {
  58.     OH_LOG_ERROR(LOG_APP, "JSVM CreateJsCore the number of params must be one");
  59.     return nullptr;
  60.   }
  61.   g_napiEnvMap[ENVTAG_NUMBER] = env1;
  62.   g_taskQueueMap[ENVTAG_NUMBER] = deque<Task *>{};
  63.   // 将TS侧传入的回调函数与env对应存储方便后续调用
  64.   //napi_ref生命周期由开发者自己管理,需要手动delete,引用 napi_value 的抽象。这允许用户管理 JavaScript 值的生命周期,包括明确定义它们的最小生命周期。
  65.   napi_ref callFun;
  66.   //为Object创建一个reference,以延长其生命周期。调用者需要自己管理reference生命周期。
  67.   napi_create_reference(env1, argv[0], 1, &callFun);
  68.   g_callBackMap[ENVTAG_NUMBER] = callFun;
  69.   napi_value coreID = 0;
  70.   {
  71.     //互斥锁
  72.     std::lock_guard<std::mutex> lock_guard(envMapLock);
  73.     CreateArkJSContext();
  74.     //累加
  75.     napi_create_uint32(env1, ENVTAG_NUMBER, &coreID);
  76.     ENVTAG_NUMBER++;
  77.   }
  78.   OH_LOG_ERROR(LOG_APP, "JSVM CreateJsCore END");
  79.   return coreID;
  80. }
复制代码
JS代码执行,在虚拟机环境中编译并运行JS代码:

  • 通过OH_JSVM_OpenHandleScope开启新的作用域。
  • 通过OH_JSVM_CompileScript对传入的JS代码进行编译。
  • 通过OH_JSVM_RunScript运行JS代码。
  • 通过OH_JSVM_CloseHandleScope关闭作用域。
  1. static std::mutex mutexLock;
  2. // 对外提供执行JS代码接口,通过coreID在对应的JSVN环境中执行JS代码
  3. static napi_value EvalUateJS(napi_env env, napi_callback_info info) {
  4.   OH_LOG_ERROR(LOG_APP, "JSVM EvalUateJS START");
  5.   size_t argc = 2;
  6.   napi_value args[2] = {nullptr};
  7.   napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
  8.   uint32_t envId;
  9.   napi_status status = napi_get_value_uint32(env, args[0], &envId);
  10.   if (status != napi_ok) {
  11.     OH_LOG_ERROR(LOG_APP, "EvalUateJS first param should be number");
  12.     return nullptr;
  13.   }
  14.  
  15.   if (g_envMap.count(envId) == 0 || g_envMap[envId] == nullptr) {
  16.     OH_LOG_ERROR(LOG_APP, "EvalUateJS env is null");
  17.     return nullptr;
  18.   }
  19.  
  20.   //获取传入的JS代码,转char[]类型
  21.   std::string dataStr = napiValueToString(env, args[1]);
  22.   napi_value res = nullptr;
  23.  
  24.   std::lock_guard<std::mutex> lock_guard(mutexLock);
  25.   {
  26.     // open handle scope
  27.     JSVM_HandleScope handlescope;
  28.  
  29.     //****1**** 开启新作用域
  30.     OH_JSVM_OpenHandleScope(*g_envMap[envId], &handlescope);
  31.     // compile js
  32.     JSVM_Value sourcecodevalue;
  33.     // 创建一个由utf-8转的JS字符串
  34.     OH_JSVM_CreateStringUtf8(*g_envMap[envId], dataStr.c_str(), dataStr.size(), &sourcecodevalue);
  35.     JSVM_Script script;
  36.  
  37.     //****2****编译一串JS代码,并返回编译后脚本
  38.     OH_JSVM_CompileScript(*g_envMap[envId], sourcecodevalue, nullptr, 0, true, nullptr, &script);
  39.     // run JS
  40.     JSVM_Value result;
  41.  
  42.     //****3****执行一段JS代码并返回结果
  43.     OH_JSVM_RunScript(*g_envMap[envId], script, &result);
  44.     JSVM_ValueType type;
  45.     //调用typeof运算符的行为,得到结果类型
  46.     OH_JSVM_Typeof(*g_envMap[envId], result, &type);
  47.     OH_LOG_INFO(LOG_APP, "JSVM API TEST type: %{public}d", type);
  48.     // Execute tasks in the current env event queue
  49.     //执行并删除任务队列中的任务
  50.     while (!g_taskQueueMap[envId].empty()) {
  51.       auto task = g_taskQueueMap[envId].front();
  52.       g_taskQueueMap[envId].pop_front();
  53.       task->Run();
  54.       delete task;
  55.     }
  56.    
  57.     if (type == JSVM_STRING) {
  58.       //JSVM_Value转字符串
  59.       std::string stdResult = fromOHStringValue(*g_envMap[envId], result);
  60.       napi_create_string_utf8(env, stdResult.c_str(), stdResult.length(), &res);
  61.     } else if (type == JSVM_BOOLEAN) {
  62.       bool ret = false;
  63.       std::string stdResult;
  64.       OH_JSVM_GetValueBool(*g_envMap[envId], result, &ret);
  65.       ret ? stdResult = "true" : stdResult = "false";
  66.       napi_create_string_utf8(env, stdResult.c_str(), stdResult.length(), &res);
  67.     } else if (type == JSVM_NUMBER) {
  68.       int32_t num;
  69.       OH_JSVM_GetValueInt32(*g_envMap[envId], result, &num);
  70.       std::string stdResult = std::to_string(num);
  71.       napi_create_string_utf8(env, stdResult.c_str(), stdResult.length(), &res);
  72.     } else if (type == JSVM_OBJECT) {
  73.       JSVM_Value objResult;
  74.       OH_JSVM_JsonStringify(*g_envMap[envId], result, &objResult);
  75.       std::string stdResult = fromOHStringValue(*g_envMap[envId], objResult);
  76.       napi_create_string_utf8(env, stdResult.c_str(), stdResult.length(), &res);
  77.     }
  78.  
  79.     //****4****关闭传入的作用域
  80.     OH_JSVM_CloseHandleScope(*g_envMap[envId], handlescope);
  81.   }
  82.   OH_LOG_ERROR(LOG_APP, "JSVM EvalUateJS END");
  83.   return res;
  84. }
复制代码
JS 运行环境烧毁
关闭和烧毁虚拟机和上下文环境的作用域与实例。

  • 利用OH_JSVM_CloseEnvScope关闭上下文环境作用域。
  • 利用OH_JSVM_DestroyEnv烧毁上下文环境。
  • 利用OH_JSVM_CloseVMScope关闭虚拟机作用域。
  • 利用OH_JSVM_DestroyVM烧毁虚拟机实例。
  1. // 对外提供释放JSVM环境接口,通过envId释放对应环境
  2. static napi_value ReleaseJsCore(napi_env env1, napi_callback_info info) {
  3.   OH_LOG_ERROR(LOG_APP, "JSVM ReleaseJsCore START");
  4.   size_t argc = 1;
  5.   napi_value argv[1];
  6.   napi_get_cb_info(env1, info, &argc, argv, nullptr, nullptr);
  7.   if (argc < 1) {
  8.     OH_LOG_ERROR(LOG_APP, "JSVM ReleaseJsCore the number of params must be one");
  9.     return nullptr;
  10.   }
  11.  
  12.   uint32_t coreEnvId;
  13.   napi_status status = napi_get_value_uint32(env1, argv[0], &coreEnvId);
  14.   if (status != napi_ok) {
  15.     OH_LOG_ERROR(LOG_APP, "JSVM CreateJsCore napi_get_value_uint32 faild");
  16.     return nullptr;
  17.   }
  18.   //是否存在创建的env环境
  19.   if (g_envMap.count(coreEnvId) == 0 ) {
  20.     OH_LOG_ERROR(LOG_APP, "JSVM CreateJsCore not has env ");
  21.     return nullptr;
  22.   }
  23.  
  24.   if (g_envMap[coreEnvId] != nullptr) {
  25.     // 互斥锁
  26.     std::lock_guard<std::mutex> lock_guard(envMapLock);
  27.  
  28.     //****1****关闭指定的Env的环境作用域
  29.     OH_JSVM_CloseEnvScope(*g_envMap[coreEnvId], g_envScopeMap[coreEnvId]);
  30.     //map中删除这个环境作用域
  31.     g_envScopeMap.erase(coreEnvId);
  32.  
  33.     //****2****销毁指定Env环境
  34.     OH_JSVM_DestroyEnv(*g_envMap[coreEnvId]);
  35.     //map中删除这个环境
  36.     g_envMap[coreEnvId] = nullptr;
  37.     g_envMap.erase(coreEnvId);
  38.  
  39.     //****3**** 关闭指定的VM的作用域
  40.     OH_JSVM_CloseVMScope(*g_vmMap[coreEnvId], g_vmScopeMap[coreEnvId]);
  41.     g_vmScopeMap.erase(coreEnvId);
  42.  
  43.     //****4****销毁指定VM实例
  44.     OH_JSVM_DestroyVM(*g_vmMap[coreEnvId]);
  45.     g_vmMap[coreEnvId] = nullptr;
  46.     g_vmMap.erase(coreEnvId);
  47.  
  48.     //删除回调
  49.     delete [] g_callBackStructMap[coreEnvId];
  50.     g_callBackStructMap[coreEnvId] = nullptr;
  51.     g_callBackStructMap.erase(coreEnvId);
  52.  
  53.     //删除引用计数
  54.     napi_delete_reference(env1, g_callBackMap[coreEnvId]);
  55.     g_callBackMap.erase(coreEnvId);
  56.     g_taskQueueMap.erase(coreEnvId);
  57.   }
  58.   OH_LOG_ERROR(LOG_APP, "JSVM ReleaseJsCore END");
  59.   return nullptr;
  60. }
复制代码
JS 中调用的 C++ 代码实现
调用的C++方法类似于NAPI的写法,通过OH_JSVM_GetCbInfo获取JS传入方法的参数,将获取到的JSVM_Value的范例转换成c的范例。
自界说C++代码中的JSVM的常用接口与NAPI的接口类似,可参考文档底部的官方文档。
同步方法
用于打印日记的简单同步方法:
  1. // 自定义Consoleinfo方法
  2. static JSVM_Value Consoleinfo(JSVM_Env env, JSVM_CallbackInfo info) {
  3.   size_t argc = 1;
  4.   JSVM_Value args[1];
  5.   char log[256] = "";
  6.   size_t log_length;
  7.   OH_JSVM_GetCbInfo(env, info, &argc, args, NULL, NULL);
  8.  
  9.   OH_JSVM_GetValueStringUtf8(env, args[0], log, 255, &log_length);
  10.   log[255] = 0;
  11.   OH_LOG_INFO(LOG_APP, "JSVM API TEST: %{public}s", log);
  12.   return nullptr;
  13. }
复制代码
用于相加的简单同步方法:
  1. // 自定义Add方法
  2. static JSVM_Value Add(JSVM_Env env, JSVM_CallbackInfo info) {
  3.   size_t argc = 2;
  4.   JSVM_Value args[2];
  5.   OH_JSVM_GetCbInfo(env, info, &argc, args, NULL, NULL);
  6.   double num1, num2;
  7.   OH_JSVM_GetValueDouble(env, args[0], &num1);
  8.   OH_JSVM_GetValueDouble(env, args[1], &num2);
  9.   JSVM_Value sum = nullptr;
  10.   OH_JSVM_CreateDouble(env, num1 + num2, &sum);
  11.   return sum;
  12. }
复制代码
异步方法
Promise方式的异步调用,主要调用OH_JSVM_CreatePromise创建一个延迟对象和一个JavaScript promise,OH_JSVM_ResolveDeferred通过与之关联的延迟对象来解析JavaScript promise,用于解析对应的可用的延迟对象的JavaScript Promise
  1. // 自定义创建Promise方法用以在JS代码中创建Promise
  2. static JSVM_Value CreatePromise(JSVM_Env env, JSVM_CallbackInfo info) {
  3.   OH_LOG_INFO(LOG_APP, "JSVM API TEST: CreatePromise start");
  4.   int envID = 0;
  5.   // 通过当前env获取envID
  6.   for (auto it = g_envMap.begin(); it != g_envMap.end(); ++it) {
  7.     if (*it->second == env) {
  8.       envID = it->first;
  9.       break;
  10.     }
  11.   }
  12.   if (envID == -1) {
  13.     OH_LOG_INFO(LOG_APP, "JSVM API TEST: CreatePromise envID faild");
  14.     return nullptr;
  15.   }
  16.   JSVM_Value promise;
  17.   JSVM_Deferred deferred;
  18.   OH_JSVM_CreatePromise(env, &deferred, &promise);
  19.   // 设计ReadTask类用以将promise对象的deferred加入执行队列
  20.   class ReadTask : public Task {
  21.     public:
  22.         ReadTask(JSVM_Env env, JSVM_Deferred deferred, int envNum) : env_(env), envID_(envNum), deferred_(deferred) {}
  23.   void Run() override {
  24.     //string str = "TEST RUN OH_JSVM_ResolveDeferred";
  25.     int envID = 0;
  26.     for (auto it = g_envMap.begin(); it != g_envMap.end(); ++it) {
  27.       if (*it->second == env_) {
  28.         envID = it->first;
  29.         break;
  30.       }
  31.     }
  32.     OH_LOG_INFO(LOG_APP, "JSVM API TEST: CreatePromise %{public}d", envID);
  33.     JSVM_Value result;
  34.     OH_JSVM_CreateInt32(env_, envID, &result);
  35.     OH_JSVM_ResolveDeferred(env_, deferred_, result);
  36.   }
  37.   private:
  38.     JSVM_Env env_;
  39.   int envID_;
  40.   JSVM_Deferred deferred_;
  41. };
  42. g_taskQueueMap[envID].push_back(new ReadTask(env, deferred, envID));
  43. OH_LOG_INFO(LOG_APP, "JSVM API TEST: CreatePromise end");
  44. return promise;
  45. }
复制代码
callback方式的异步调用,需要调用NAPI的异步调用方法。
  1. // 用以在native层中调用TS侧传入的Callback函数
  2. static JSVM_Value OnJSResultCallback(JSVM_Env env, JSVM_CallbackInfo info) {
  3.   size_t argc = 3;
  4.   JSVM_Value args[3];
  5.   JSVM_CALL(env, OH_JSVM_GetCbInfo(env, info, &argc, args, NULL, NULL));
  6.   int callId = 0;
  7.   OH_JSVM_GetValueInt32(env, args[0], &callId);
  8.   napi_value callArgs[2] = {nullptr, nullptr};
  9.   size_t size;
  10.   size_t size1;
  11.  
  12.   OH_JSVM_GetValueStringUtf8(env, args[1], nullptr, 0, &size);
  13.   char Str1[size + 1];
  14.   OH_JSVM_GetValueStringUtf8(env, args[1], Str1, size + 1, &size);
  15.  
  16.   OH_JSVM_GetValueStringUtf8(env, args[2], nullptr, 0, &size1);
  17.   char Str2[size1 + 1];
  18.   OH_JSVM_GetValueStringUtf8(env, args[2], Str2, size1 + 1, &size1);
  19.  
  20.   napi_create_string_utf8(g_napiEnvMap[callId], Str1, size + 1, &callArgs[0]);
  21.   napi_create_string_utf8(g_napiEnvMap[callId], Str2, size1 + 1, &callArgs[1]);
  22.   napi_value callback = nullptr;
  23.   // 通过callId获取在创建当前JSVM环境时传入的TS回调方法
  24.   napi_get_reference_value(g_napiEnvMap[callId], g_callBackMap[callId], &callback);
  25.   napi_value ret;
  26.   // 执行TS回调方法
  27.   napi_call_function(g_napiEnvMap[callId], nullptr, callback, 2, callArgs, &ret);
  28.   char retStr[256];
  29.   napi_get_value_string_utf8(g_napiEnvMap[callId], ret, retStr, 256, &size);
  30.  
  31.   JSVM_Value returnVal;
  32.   OH_JSVM_CreateStringUtf8(env, retStr, JSVM_AUTO_LENGTH, &returnVal);
  33.   return returnVal;
  34. }
复制代码
OH_JSVM_StrictEquals提供类似调用严酷相等算法的行为。
  1. // 自定义AssertEqual方法
  2. static JSVM_Value AssertEqual(JSVM_Env env, JSVM_CallbackInfo info) {
  3.   size_t argc = 2;
  4.   JSVM_Value args[2];
  5.   JSVM_CALL(env, OH_JSVM_GetCbInfo(env, info, &argc, args, NULL, NULL));
  6.  
  7.   bool isStrictEquals = false;
  8.   OH_JSVM_StrictEquals(env, args[0], args[1], &isStrictEquals);
  9.  
  10.   if (isStrictEquals) {
  11.     OH_LOG_INFO(LOG_APP, "JSVM API TEST RESULT: PASS");
  12.   } else {
  13.     OH_LOG_INFO(LOG_APP, "JSVM API TEST RESULT: FAILED");
  14.   }
  15.   return nullptr;
  16. }
复制代码
代码中利用的全局变量和工具方法,包含自界说的全局变量和工具方法。
  1. #include "napi/native_api.h"
  2. #include "ark_runtime/jsvm.h"
  3. #include "common.h"
  4. #include <bits/alltypes.h>
  5. #include <deque>
  6. #include <map>
  7. #include <unistd.h>
  8. #include <hilog/log.h>
  9. #include <cstring>
  10. #include <string>
  11. #include <vector>
  12. #include <sstream>
  13. #define LOG_TAG "TEST_TAG"
  14. using namespace std;
  15. // 定义map管理每个独立vm环境
  16. static map<int, JSVM_VM*> g_vmMap;
  17. static map<int, JSVM_VMScope> g_vmScopeMap;
  18. static map<int, JSVM_Env*> g_envMap;
  19. static map<int, napi_env> g_napiEnvMap;
  20. static map<int, JSVM_EnvScope> g_envScopeMap;
  21. static map<int, napi_ref> g_callBackMap;
  22. static map<int, JSVM_CallbackStruct*> g_callBackStructMap;
  23. static uint32_t ENVTAG_NUMBER = 0;
  24. static std::mutex envMapLock;
  25. static int aa = 0;
  26. class Task {
  27.   public:
  28.     //virtual虚析构函数在销毁时会调用对象的析构函数,这样就不会出现像有的数据成员没有销毁导致内存泄露的问题或者程序直接崩溃
  29.     virtual ~Task() = default;
  30.   // virtual纯虚函数的类是一个抽象类
  31.   virtual void Run() = 0;
  32. };
  33. //deque双端存储队列
  34. static map<int, deque<Task *>> g_taskQueueMap;
  35. std::string napiValueToString(napi_env env, napi_value nValue) {
  36.   size_t buffLen = 0;
  37.   napi_get_value_string_utf8(env, nValue, nullptr, 0, &buffLen);
  38.   char buffer[buffLen + 1];
  39.   napi_get_value_string_utf8(env, nValue, buffer, buffLen + 1, &buffLen);
  40.  
  41.   return buffer;
  42. }
  43. static std::string fromOHStringValue(JSVM_Env &env, JSVM_Value &value) {
  44.   size_t size;
  45.   JSVM_Status status;
  46.   status = OH_JSVM_GetValueStringUtf8(env, value, nullptr, 0, &size);
  47.   char resultStr[size + 1];
  48.   status = OH_JSVM_GetValueStringUtf8(env, value, resultStr, size + 1, &size);
  49.   return resultStr;
  50. }
  51. // create_jsvm_runtime.cpp
  52. EXTERN_C_START
  53. static napi_value Init(napi_env env, napi_value exports) {
  54.   napi_property_descriptor desc[] = {
  55.     {"createJsCore", nullptr, CreateJsCore, nullptr, nullptr, nullptr, napi_default, nullptr},
  56.   {"releaseJsCore", nullptr, ReleaseJsCore, nullptr, nullptr, nullptr, napi_default, nullptr},
  57.   {"evalUateJS", nullptr, EvalUateJS, nullptr, nullptr, nullptr, napi_default, nullptr}
  58. };
  59. napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
  60. return exports;
  61. }
  62. EXTERN_C_END
  63. static napi_module demoModule = {
  64.   .nm_version = 1,
  65.   .nm_flags = 0,
  66.   .nm_filename = nullptr,
  67.   .nm_register_func = Init,
  68.   .nm_modname = "entry",
  69.   .nm_priv = ((void *)0),
  70.   .reserved = {0},
  71. };
  72. extern "C" __attribute__((constructor)) void RegisterEntryModule(void) { napi_module_register(&demoModule); }
复制代码
场景二: JSVM 读取并执行文件的 JS 代码

一些场景需要去读取文件中的JS代码,可将JS代码放到/resources/rawfile/目次下,利用读取资源文件相干功能,将读取到的字符串,利用场景一的方式在JSVM执行JS代码,并回调C++代码。
从资源文件中读取 JS 代码
xxx.js文件放在/resources/rawfile/目次下
xxx.js:
  1. {
  2.   let a = "hello World";
  3.   consoleinfo(a);
  4.   const mPromise = createPromise();
  5.   mPromise.then((result) => {
  6.     assertEqual(result, 0);
  7.     onJSResultCallback(result, "abc", "v");
  8.   });
  9.   a;
  10. };
复制代码
配置cmakelists:
  1. target_link_libraries(entry PUBLIC libace_napi.z.so libhilog_ndk.z.so librawfile.z.so)
复制代码
ArkTS代码:
获取资源对象,调用native的方法传入资源对象和文件名称。
  1. let resmgr = getContext().resourceManager
  2. let a = testNapi.getRawFileContent(resmgr,"xxx.js")
复制代码
Native代码:
获取ArkTS传入的资源对象转成native对象,获取rawfile指针对象,获取rawfile巨细并申请内存,然后读取文件内容,最后关闭指针,将获取到的内容去除\r。
  1. #include "napi/native_api.h"
  2. #include <hilog/log.h>
  3. #include <memory>
  4. #include <rawfile/raw_file_manager.h>
  5. static std::string str_txt;
  6. static napi_value GetRawFileContent(napi_env env, napi_callback_info info)
  7. {
  8.   OH_LOG_INFO(LOG_APP, "GetFileContent Begin");
  9.   size_t requireArgc = 3;
  10.   size_t argc = 2;
  11.   napi_value argv[2] = { nullptr };
  12.   // 获取参数信息
  13.   napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
  14.  
  15.   // argv[0]即为函数第一个参数Js资源对象,OH_ResourceManager_InitNativeResourceManager转为Native对象。
  16.   NativeResourceManager *mNativeResMgr = OH_ResourceManager_InitNativeResourceManager(env, argv[0]);
  17.   size_t strSize;
  18.   char strBuf[256];
  19.   napi_get_value_string_utf8(env, argv[1], strBuf, sizeof(strBuf), &strSize);
  20.   std::string filename(strBuf, strSize);
  21.  
  22.   // 获取rawfile指针对象
  23.   RawFile *rawFile = OH_ResourceManager_OpenRawFile(mNativeResMgr, filename.c_str());
  24.   if (rawFile != nullptr) {
  25.     OH_LOG_INFO(LOG_APP, "OH_ResourceManager_OpenRawFile success");
  26.   }
  27.   // 获取rawfile大小并申请内存
  28.   long len = OH_ResourceManager_GetRawFileSize(rawFile);
  29.   std::unique_ptr<uint8_t[]> data= std::make_unique<uint8_t[]>(len);
  30.  
  31.   // 一次性读取rawfile全部内容
  32.   int res = OH_ResourceManager_ReadRawFile(rawFile, data.get(), len);
  33.  
  34.   // 多次部分读取rawfile, 每次读取100 Byte。获取全部内容
  35.   // long offset = 0;
  36.   // while (OH_ResourceManager_GetRawFileRemainingLength(rawFile) > 0) {
  37.   //     OH_ResourceManager_ReadRawFile(rawFile, data.get() + offset, 100);
  38.   //     offset += 100;
  39.   // }
  40.  
  41.   // 关闭打开的指针对象
  42.   OH_ResourceManager_CloseRawFile(rawFile);
  43.   OH_ResourceManager_ReleaseNativeResourceManager(mNativeResMgr);
  44.   // 转为JS对象
  45.  
  46.   std::string str(reinterpret_cast<char *>(data.get()), len);
  47.   size_t pos = 0;
  48.   while ((pos = str.find('\r')) != std::string::npos) {
  49.   str.replace(pos, 1, "");
  50. }
  51.   str_txt = str;
  52.  
  53.   return nullptr;
  54. }
复制代码
JSVM 执行 JS 代码
总体与场景一雷同,只需修改JS代码,执行这段代码里获取ArkTS传入的JS字符串相干代码,将其更换为上面从JS文件中获取到的JS字符串。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

曹旭辉

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表