0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

OpenHarmony源码剖析之ACE(JavaScript运行环境初始化)

OpenAtom OpenHarmony 来源:51CTO 作者:张亮亮 2021-11-18 10:40 次阅读

张亮亮

深圳开鸿数字产业发展有限公司

简介

JS UI 框架引擎ACE全称 Ability Cross-platform Environment,是 OpenHarmony 标准系统上的UI框架。ACE:结合了 OpenHarmony 系统的基础组件 Ability,开源 jsframework 框架,开源 js 引擎 quickjs,开源跨平台 UI 框架 flutter,开源渲染引擎 skia 以及各种平台能力 API 等共同构筑了 OpenHarmony 标准系统 javacript 应用开发的基础。

1.1 OpenHarmony 架构图

9b9230c6-47d2-11ec-b939-dac502259ad0.png

1.2 ACE UI框架引擎图

9cadf030-47d2-11ec-b939-dac502259ad0.png

1.3 ACE 主要构成

1. JavaScript前端框架

目前 OpenHarmony 标准系统采用主流的类 Web 范式,对开源的 Weex 框架中的 jsframework 做定制化,采用 ts 开发,主要包括编程模型 MVVM、组件、API、页面路由以及事件处理。

2. JavaScript引擎

目前 OpenHarmony 标准系统使用的是开源 quickjs 引擎,提供 JS 语言运行时和执行上下文,提供 js 的解析和 jsframework 的加载。

3. 中间转换层

中间转换层也就是 JS 桥接层,实现前端开发框架到 UI 后端引擎和 JS 引擎的对接。

4. 声明式UI后端引擎

C++构建的UI后端引擎,包括 UI 组件、布局视图、动画事件、自绘制选软管线。

5. 渲染引擎

目前 OpenHarmony 标准系统复用了开源跨平台 UI 框架 flutter 引擎提供基础的图形渲染能力。

6. 平台适配层

目前 OpenHarmony 标准系统的适配层完成了 OpenHarmony 平台和 IDE 的 previewer 的适配,将平台依赖聚焦到平台相关的画布、通用线程以及事件处理机制等少数接口上,为跨平台提供相应的基础设施,实现跨平台一致化的 UI 渲染。

7. 能力扩展层

为扩展 ACE 能力提供的插件机制,平台其他子系统可以利用插件机制开发相关能力的 js 接口,为应用层提供相应的能力支持。通过 napi 提供引擎无关的插件实现机制,保证接口的 ABI 兼容性。

基础知识

2.1 代码结构
/foundation/ace/ace_engine├── adapter                       # 平台适配目录│   ├── common|   ├── ohos                      # ohos平台适配目录│   └── preview                   # IDE preview平台适配目录├── frameworks                    # 框架代码│   ├── base                      # 基础库│   ├── bridge                    # 前后端对接层│   └── core                      # 声明式UI后端引擎目录/third_party/jsframework          # JavaScript前端框架/third_party/quickjs              # JavaScript引擎/third_party/flutter#flutterengine提供跨平台自渲染引擎

2.2 ACE框架类图

9ce025a0-47d2-11ec-b939-dac502259ad0.png

(点击图片查看高清大图) 2.3 线程模型

ACE JS 应用启动时会创建一系列线程,形成独立的线程模型,以实现高性能的渲染流程。

Platform线程:当前平台的主线程,也就是应用的主线程,主要负责平台层的交互、应用生命周期以及窗口环境的创建。

JS线程:JS 前端框架的执行线程,应用的 JS 逻辑以及应用 UI 界面的解析构建都在该线程执行。

UI线程:引擎的核心线程,组件树的构建以及整个渲染管线的核心逻辑都在该线程:包括渲染树的构建、布局、绘制以及动画调度。

GPU线程:现代的渲染引擎,为了充分发挥硬件性能,都支持 GPU 硬件加速,在该线程上,会通过系统的窗口句柄,创建 GPU 加速的 OpenGL 环境,负责将整个渲染树的内容光栅化,直接将每一帧的内容渲染合成到该窗口的 Surface 上并送显。

IO线程:主要为了异步的文件 IO 读写,同时该线程会创建一个离屏的 GL 环境,这个环境和 GPU 线程的 GL 环境是同一个共享组,可以共享资源,图片资源解码的内容可直接在该线程上传生成 GPU 纹理,实现更高效的图片渲染。

Javascript运行环境初始化

3.1 时序图

9d28c008-47d2-11ec-b939-dac502259ad0.png

(点击图片查看高清大图)

3.2 源码解析

1.OnStart()

  • AceAbility继承自Ability,当应用启动时首先应用程序框架会调用AceAbility的生命周期函数OnStart();

  • 通过Ability的api获取Hap包的路径,通过读取配置文件manifest.json获取应用程序配置的前端的类型;

  • 当前我们只关注JS前端,目前支持的前端类型包括enum class FrontendType { JSON, JS, JS_CARD, DECLARATIVE_JS };

  • 根据前端类型调用ACE核心聚合类AceContainer的静态方法CreateContainer,这个类是ACE框架平台适配层的核心类,接下来的前端核心类和JS引擎的创建都是在其中完成的。

//foundationaceace_engineadapterpreviewentranceace_ability.cppvoid AceAbility::OnStart(const Want& want){    ...        //获取打包的js bundle文件,取出配置文件,获取前端类型    auto packagePathStr = GetBundleCodePath();    auto moduleInfo = GetHapModuleInfo();    if (moduleInfo != nullptr) {        packagePathStr += "/" + moduleInfo->name + "/";    }    FrontendType frontendType = GetFrontendTypeFromManifest(packagePathStr);
    // create container    //创建AceContainer:内部初始化时创建了js线程,load了js engine,创建了JsFrontend    Platform::CreateContainer(        abilityId_, frontendType, this,        std::make_unique([this]() {            TerminateAbility();        }));    ...    ...
    //运行page    Platform::RunPage(        abilityId_, Platform::GetContainer(abilityId_)->GeneratePageId(),        parsedPageUrl, want.GetStringParam(START_PARAMS_KEY));
    ...}
2. CreateContainer
  • 在AceContainer::CreateContainer中,首先创建了AceContainer对象;

  • 在构造函数中创建了FlutterTaskerExecutor对象用于多线程的任务管理;

  • 此处主要关注JS线程的创建和初始化,在InitJsThread()中创建了JS线程并获取保存了jsRunner_用于JS任务的派发;

//foundationaceace_engineadapterpreviewentranceace_container.cppvoid AceContainer::CreateContainer(int32_t instanceId, FrontendType type, AceAbility* aceAbility,    std::unique_ptr callback){    auto aceContainer = AceType::MakeRefPtr(instanceId, type, aceAbility, std::move(callback));    AceEngine::Get().AddContainer(instanceId, aceContainer);    auto front = aceContainer->GetFrontend();    if (front) {        front->UpdateState(Frontend::ON_CREATE);        front->SetJsMessageDispatcher(aceContainer);    }}
AceContainer::AceContainer(int32_t instanceId, FrontendType type, AceAbility* aceAbility,    std::unique_ptr callback) : instanceId_(instanceId), type_(type), aceAbility_(aceAbility){    ACE_DCHECK(callback);    //创建和初始化FlutterTaskerExecutor用于封装管理Flutter ACE中涉及的多个线程:platform,UI,JS,GPU,IO,统一post任务到各线程执行    auto flutterTaskExecutor = Referenced::MakeRefPtr();    //初始化platform线程,将flutter platform线程的TaskerRunner适配到ohos平台主线程的EventRunner    flutterTaskExecutor->InitPlatformThread();    //初始化JS线程,这个线程用于解析JS,不归flutter管理,因此是单独在ACE里使用的    flutterTaskExecutor->InitJsThread();    //taskExector_封装了所有线程任务调度的接口,因此会传给Frontend用于JS前端解析任务和PipelineContext后端渲染UI和GPU IO相关    taskExecutor_ = flutterTaskExecutor;    if (type_ != FrontendType::DECLARATIVE_JS) {        //zll:初始化前端        InitializeFrontend();    }
    platformEventCallback_ = std::move(callback);}
void FlutterTaskExecutor::InitJsThread(bool newThread){    //创建并初始化JS线程,获取保存js线程的TaskRunner    //JS线程是ACE平台特有,不通过flutter创建管理    if (newThread) {        jsThread_ = std::make_unique(GenJsThreadName());        jsRunner_ = jsThread_->GetTaskRunner();    } else {        jsRunner_ = uiRunner_;    }}

3. JsFrontend

  • 完成JS线程的初始化后,如果前端类型不是DECLARATIVE_JS,会调用InitializeFrontend()对前端进行初始化。

  • 首先创建前端对象,Frontend::Create定义在js_frontend.cpp中,创建的是JsFrontend实例;

  • 然后通过JsEngineLoader::Get()动态加载QjsEngineLoader;

  • 再通过QjsEngineLoader创建QjsEngine并设置给JsFrontend;最后对JsFrontend对象做初始化;

  • JsFrontend是ACE框架从后端进入前端的唯一入口,AceAbility、AceContainer和JsFrontend是一一对应的关系;

//foundationaceace_engineadapterpreviewentranceace_container.cppvoid AceContainer::InitializeFrontend(){    if (type_ == FrontendType::JS) {        //目前Frontend::Create定义在js_frontend.cpp中,创建的是JsFrontend实例        frontend_ = Frontend::Create();        auto jsFrontend = AceType::DynamicCast(frontend_);        //创建并初始化js engine,此处是通过dlopen加载的qjs engine管理对象        jsFrontend->SetJsEngine(Framework::Get().CreateJsEngine(instanceId_));        ...    } else if (type_ == FrontendType::DECLARATIVE_JS) {        ...    } else {        ...    }    ACE_DCHECK(frontend_);    //初始化前端和js engine    frontend_->Initialize(type_, taskExecutor_);}
4. QjsEngine
  • 接下来我们继续分析一下JS引擎管理对象的创建;

  • 首先通过dlopen动态加载libace_engine_qjs.z.so通过入口函数创建获取QjsEngineLoader单例对象;

  • 然后通过QjsEngineLoader::CreateJsEngine()创建QjsEngine;

//foundationaceace_engineframeworksridgejs_frontendenginecommonjs_engine_loader.cpp//"libace_engine_qjs.z.so"动态库的入口,在qjs_engine_loader.cpp中定义constexpr char JS_ENGINE_ENTRY[] = "OHOS_ACE_GetJsEngineLoader";constexpr char QUICK_JS_ENGINE_SHARED_LIB[] = "libace_engine_qjs.z.so";...const char* GetSharedLibrary(){    return QUICK_JS_ENGINE_SHARED_LIB;}JsEngineLoader& GetJsEngineLoader(const char* sharedLibrary){    void* handle = dlopen(sharedLibrary, RTLD_LAZY);    ...    //    auto loader = reinterpret_cast(entry());    ...
    return *loader;}...//通过加载动态链接库的形式获取qjs桥接模块的入口函数并创建QjsEngineLoaderJsEngineLoader& JsEngineLoader::Get(){    static JsEngineLoader& instance = GetJsEngineLoader(GetSharedLibrary());    return instance;}
//foundationaceace_engineframeworksridgejs_frontendenginequickjsqjs_engine_loader.cppRefPtr QjsEngineLoader::CreateJsEngine(int32_t instanceId) const{    return AceType::MakeRefPtr(instanceId);}
5. JS线程
  • 在完成了QjsEngine的创建并设置给JsFrontend后,调用JsFrontend::Initialize();

  • 这里主要完成了FrontendDelegateImpl对象的创建和初始化将对JS引擎的相关操作委派给这个对象;

  • 以及Post JS引擎初始化的任务到JS线程的TaskRunner的message queue;

//foundationaceace_engineframeworksridgejs_frontendjs_frontend.cppbool JsFrontend::Initialize(FrontendType type, const RefPtr& taskExecutor){    LOGI("JsFrontend initialize begin.");    type_ = type;    ACE_DCHECK(type_ == FrontendType::JS);    //创建并初始化FrontendDelegate对象,具体实现为FrontendDelegateImpl    InitializeFrontendDelegate(taskExecutor);    //在JS线程初始化js engine,真正的启动JS引擎运行时并创建上下文    taskExecutor->PostTask(        [weakEngine = WeakPtr(jsEngine_), delegate = delegate_] {            auto jsEngine = weakEngine.Upgrade();            if (!jsEngine) {                return;            }            jsEngine->Initialize(delegate);        },        TaskExecutor::JS);
    LOGI("JsFrontend initialize end.");    return true;}
6. jsframework
  • 在JS线程执行QjsEngine对象的初始化,初始化JS运行环境;

  • 初始化JS引擎运行时上下文和初始化JavaScript框架层jsframework;

//foundationaceace_engineframeworksridgejs_frontendenginequickjsqjs_engine.cppbool QjsEngine::Initialize(const RefPtr& delegate){    ACE_SCOPED_TRACE("QjsEngine::Initialize");    LOGI("Initialize");
    JSRuntime* runtime = nullptr;    JSContext* context = nullptr;
    // put JS_NewContext as early as possible to make stack_top in context    // closer to the top stack frame pointer of JS thread.    runtime = JS_NewRuntime();    if (runtime != nullptr) {        context = JS_NewContext(runtime);    }
    ...
    engineInstance_ = AceType::MakeRefPtr(delegate, instanceId_);    return engineInstance_->InitJsEnv(runtime, context);}
bool QjsEngineInstance::InitJsEnv(JSRuntime* runtime, JSContext* context){    ...    context_ = context;    //1.初始化js运行时,上下文    if (!InitJsContext(context_, MAX_STACK_SIZE, instanceId_, this)) {        LOGE("Qjs cannot allocate JS context");        EventReport::JS_ENGINE_INIT_ERR);        JS_FreeRuntime(runtime_);        return false;    }    ...    //2.加载JS Framework,初始化JS前端框架    //加载jsframework,js_framework和js_framework_size是quickjs编译器编译jsframework的ts生成的c文件    //quickjs通过JS_ReadObject读取生成的cbytecode,并通过JS_EvalFunction(ctx, obj)执行相应的函数    //在这里最终调用的函数是jsframework/runtime/preparation/index.ts中的initFramework()函数    JSValue retVal = LoadJsFramework(GetQjsContext(), js_framework, js_framework_size, instanceId_);    bool result = JS_IsException(retVal) ? false : true;    if (context) {        JS_FreeValue(context, retVal);    }    ...
    return result;}
7. ACE模块
  • 初始化JS引擎运行时上下文是在InitJsContext完成;

  • 其中初始化Ace模块并将模块导入JS运行时上下文中,为jsframework框架层提供了ACE功能相关的接口;

  • jsframework可以通过调用ACE模块的接口完成Page上Dom元素到后端声明式UI元素节点的创建;

  • 同时往JS运行时上下文全局对象挂载了日志打印的函数用于对接平台日志打印功能;

//foundationaceace_engineframeworksridgejs_frontendenginequickjsqjs_engine.cpp//ace模块向js context暴露的函数,(js函数名,参数个数,对应的C函数)const JSCFunctionListEntry JS_ACE_FUNCS[] = {    JS_CFUNC_DEF_CPP("domCreateBody", 5, JsDomCreateBody),    JS_CFUNC_DEF_CPP("domAddElement", 9, JsDomAddElement),    JS_CFUNC_DEF_CPP("updateElementAttrs", 3, JsUpdateElementAttrs),    JS_CFUNC_DEF_CPP("updateElementStyles", 3, JsUpdateElementStyles),    JS_CFUNC_DEF_CPP("onCreateFinish", 0, JsOnCreateFinish),    JS_CFUNC_DEF_CPP("onUpdateFinish", 0, JsOnUpdateFinish),    JS_CFUNC_DEF_CPP("removeElement", 2, JsRemoveElement),    JS_CFUNC_DEF_CPP("callNative", 1, JsCallNative),    JS_CFUNC_DEF_CPP("callComponent", 3, JsCallComponent),    JS_CFUNC_DEF_CPP("loadIntl", 0, JsLoadIntl),    JS_CFUNC_DEF_CPP("loadLocaleData", 0, JsLoadLocaleData),#ifdef ENABLE_JS_DEBUG    JS_CFUNC_DEF_CPP("compileAndRunBundle", 4, JsCompileAndRunBundle),#endif};...
bool InitJsContext(JSContext* ctx, size_t maxStackSize, int32_t instanceId, const QjsEngineInstance* qjsEngineInstance){    ...        //将ace模块注入到上下文中,使得jsframework可以通过注册的接口调用ace的相关功能    // Inject ace native functions module    InitAceModules(ctx);
    JSValue globalObj, perfUtil;    globalObj = JS_GetGlobalObject(ctx);    perfUtil = JS_NewObject(ctx);
    InitJsConsoleObject(ctx, globalObj);
    JS_SetPropertyStr(ctx, perfUtil, "printlog", JS_NewCFunction(ctx, JsPerfPrint, "printlog", 0));    JS_SetPropertyStr(ctx, perfUtil, "sleep", JS_NewCFunction(ctx, JsPerfSleep, "sleep", 1));    JS_SetPropertyStr(ctx, perfUtil, "begin", JS_NewCFunction(ctx, JsPerfBegin, "begin", 1));    JS_SetPropertyStr(ctx, perfUtil, "end", JS_NewCFunction(ctx, JsPerfEnd, "end", 1));    JS_SetPropertyStr(ctx, globalObj, "perfutil", perfUtil);
    ...
    JSValue hiView;    hiView = JS_NewObject(ctx);    JS_SetPropertyStr(ctx, hiView, "report", JS_NewCFunction(ctx, JSHiViewReport, "report", 2));    JS_SetPropertyStr(ctx, globalObj, "hiView", hiView);
    JSValue i18nPluralRules;    i18nPluralRules = JS_NewObject(ctx);    JS_SetPropertyStr(ctx, i18nPluralRules, "select", JS_NewCFunction(ctx, JsPluralRulesFormat, "select", 1));    JS_SetPropertyStr(ctx, globalObj, "i18nPluralRules", i18nPluralRules);        //将ace模块导入到cxt指定的js 上下文中    const char* str = "import * as ace from 'ace';
"                      "var global = globalThis;
"                      "global.ace = ace;
"#ifdef ENABLE_JS_DEBUG                      "global.compileAndRunBundle = ace.compileAndRunBundle;
"#endif                      "global.callNative = ace.callNative;
";
    if (JS_CALL_FAIL == CallEvalBuf(ctx, str, strlen(str), "", JS_EVAL_TYPE_MODULE, instanceId)) {        LOGW("Qjs created JS context but failed to init Ace modules!");    }
    JS_FreeValue(ctx, globalObj);    return true;}

8. 初始化完成

  • 完成JS运行时上下文初始化之后,紧接着加载初始化jsframework,为JS应用程序提供javascript应用框架;

  • 这个地方使用quickjs的运行bytecode的方法,在编译时通过qjsc(quickjs编译器)将jsframework编译成c文件;

  • c文件的内容就是一个bytecode的组数和数组大小,指定了jsframework的入口函数;

  • 在这里对应jsframework/runtime/preparation/index.ts 中的initFramework()完成jsframework的初始化;

  • jsframework初始化完成的工作主要包括挂载到js运行时上下文全局对象上提供给native调用的jsframework函数,注册到jsframework的ACE提供的模块及其中的方法和jsframework提供的与native一一对应的组件及其方法;

//foundationaceace_engineframeworksridgejs_frontendenginequickjsqjs_engine.cppJSValue LoadJsFramework(JSContext* ctx, const uint8_t buf[], const uint32_t bufSize, int32_t instanceId){    ACE_SCOPED_TRACE("LoadJsFramework");
    LOGI("Qjs loading JS framework");    //等同于调用jsframework/runtime/preparation/index.ts 中的initFramework()    JSValue ret = CallReadObject(ctx, buf, bufSize, true, instanceId);    if (JS_IsException(ret)) {        LOGD("Qjs loading JSFramework failed!");        QjsUtils::JsStdDumpErrorAce(ctx, JsErrorType::LOAD_JS_FRAMEWORK_ERROR, instanceId);    }
    return ret;}
//third_partyjsframework
untimepreparationinit.tsexport function initFramework(): void {  for (const serviceName in i18n) {    service.register(serviceName, i18n[serviceName]);  }  for (const serviceName in dpi) {    service.register(serviceName, dpi[serviceName]);  }  //jsframework提供的可供Ace native在QjsEngine对象中调用的TS方法  const globalMethods: GlobalInterface = {    'createInstance': globalApi.createInstance,    'registerModules': globalApi.registerModules,    'appDestroy': globalApi.appDestroy,    'appError': globalApi.appError,    'destroyInstance': globalApi.destroyInstance,    'getRoot': globalApi.getRoot,    'callJS': globalApi.callJS  };  //注册modules,这些modules是ACE module提供的,调用这些模块的方法,会通过调用注册到qjs_engine的ace模块中的callNative方法  //会最终通过ace module的callNative调用到qjs_engine中的JsCallNative-->JsHandleModule-->然后调用对应的native方法  // registerModules and registerComponents  ModulesInfo.forEach(modules => {    globalMethods['registerModules'](modules);  });    //注册components组件,对组件方法的调用,最终会通过ace module的callComponent调用到qjs_engine中的JsCallComponent  ComponentsInfo.forEach((name) => {    if (name && name.type && name.methods) {      NativeElementClassFactory.createNativeElementClass(        name.type,        name.methods      );    }  });  //全局方法,这些方法可以被Qjs engine通过JS_GetPropertyStr(ctx, globalObj, "methodNamge")获取并调用,从而实现了从native到js的调用  for (const methodName in globalMethods) {    global[methodName] = (...args: any) => {      const res: any = globalMethods[methodName](...args);      if (res instanceof Error) {        Log.error(res.toString());      }      return res;    };  }}

总结

以上内容首先对 OpenHarmony 标准系统上 JS UI 框架引擎 ACE 的逻辑架构及相关模块进行了简单的介绍,给出了 ACE 架构中相关模块涉及的部分类的类图,结合本次重点分析的 Javascript 运行环境初始化的时序图详细分析了 Javascript 运行环境初始化的整个流程。

本文首发自:https://harmonyos.51cto.com/posts/7908
编辑:jq


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • JS
    JS
    +关注

    关注

    0

    文章

    73

    浏览量

    17846
  • ui
    ui
    +关注

    关注

    0

    文章

    198

    浏览量

    21183
  • OpenHarmony
    +关注

    关注

    23

    文章

    3260

    浏览量

    15159

原文标题:OpenHarmony源码解析之ACE(JavaScript运行环境初始化)

文章出处:【微信号:gh_e4f28cfa3159,微信公众号:OpenAtom OpenHarmony】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    用STM32cubeMX生成的STM32F446RET6初始化代码反复复位是为什么?

    用STM32cubeMX生成的STM32F446RET6初始化代码在KEIL MDK环境运行时,总是反复复位是什么问题呢? 正常情况下,全速运行状态下,会一直在while(1)循环
    发表于 03-21 06:16

    字符型、指针型等变量等该如何初始化

     对于数值类型的变量往往初始化为0,但对于其他类型的变量,如字符型、指针型等变量等该如何初始化呢?
    的头像 发表于 03-18 11:02 205次阅读

    MCU单片机GPIO初始化该按什么顺序配置?为什么初始化时有电平跳变?

    GPIO初始化时有时钟配置、模式配置、输出配置、复用配置,那么在编写初始化代码时,到底该按什么顺序执行呢?如果顺序不当那初始化过程可能会出现短暂的电平跳变。
    的头像 发表于 02-22 11:07 361次阅读
    MCU单片机GPIO<b class='flag-5'>初始化</b>该按什么顺序配置?为什么<b class='flag-5'>初始化</b>时有电平跳变?

    串口初始化一般是初始化哪些内容

    串口初始化是指在使用串口进行数据通信之前,对串口进行一系列的设置和配置,以确保串口能够正常工作。串口初始化的内容主要包括以下几个方面: 串口硬件设置:首先,需要确定要使用的串口是哪一个,通常计算机
    的头像 发表于 01-04 09:39 641次阅读

    labview运行后如何初始化

    LabVIEW是一款强大的图形化编程软件,在运行之前通常需要进行一些初始化操作。本文将详细介绍LabVIEW运行前的初始化过程,并提供了一些常用的
    的头像 发表于 12-28 17:24 905次阅读

    javascript运行环境有哪些

    JavaScript 是一种广泛应用于网页开发的编程语言,它可以在不同的运行环境运行。以下是一些常见的 JavaScript
    的头像 发表于 11-27 16:11 1205次阅读

    OpenHarmonyNAPI框架介绍

    。 可以看到,NAPI 接口本身是 C++语言实现的,这些接口可以帮助 C++代码创建 JS 变量,或访问 JavaScript 运行环境中的 JS 变量与方法。 OpenHarmony
    发表于 11-23 15:36

    如何在博途环境下载但不重新初始化数据块呢?

    现场设备运行过程中有时候我们需要更改在线数据块的结构(比如增加一些变量),但是如果直接更改数据块并下载会导致其重新初始化
    的头像 发表于 11-10 09:25 656次阅读
    如何在博途<b class='flag-5'>环境</b>下载但不重新<b class='flag-5'>初始化</b>数据块呢?

    STM32只初始化SPIMOSI不初始化SPICLK可以用吗?

    STM32只初始化SPIMOSI,不初始化SPICLK,可以用么
    发表于 09-21 06:36

    如何解决OpenVINO工具套件环境初始化过程中遇到的错误?

    执行 setupvars.bat 脚本以初始化 Windows® 10 中的OpenVINO环境 收到错误: 输入线过长。命令的语法不正确。
    发表于 08-15 07:30

    rt-thread线程栈初始化参数分析

    RT-Thread 在线程初始化的代码内有一段初始化线程堆栈的代码
    的头像 发表于 08-14 16:50 992次阅读
    rt-thread线程栈<b class='flag-5'>初始化</b>参数分析

    KUKA机器人的初始化运行

    KUKA 机器人的初始化运行称为 BCO 运行。 BCO 是  B lock coincidence (即程序段重合)的缩写。重合意为 “ 一致 ” 及 “ 时间 / 空间事件的会合 ”。 在下
    的头像 发表于 07-17 10:42 1207次阅读
    KUKA机器人的<b class='flag-5'>初始化</b><b class='flag-5'>运行</b>

    OpenHarmony嵌入式系统原理与应用——基于RK2206芯片(微课视频版)》学习记录1 环境配置与源码编译

    一、环境布置 在进行OpenHarmony开发之前,首先需要搭建相应的开发环境。具体步骤如下: 安装Linux操作系统:OpenHarmony开发需要使用Linux操作系统,因此需要安
    发表于 06-25 11:26

    STM32H7实现BootLoader内SDRAM的初始化注意事项

    Function Implementation:在 ArtPi 的环境下,实现一个简单的 BootLoader,该 BootLoader 实现了 QSPI 和 SDRAM 功能的初始化
    的头像 发表于 06-07 15:51 2198次阅读
    STM32H7实现BootLoader内SDRAM的<b class='flag-5'>初始化</b>注意事项

    PyTorch教程6.4之惰性初始化

    电子发烧友网站提供《PyTorch教程6.4之惰性初始化.pdf》资料免费下载
    发表于 06-05 11:52 0次下载
    PyTorch教程6.4之惰性<b class='flag-5'>初始化</b>