设计网站页面的工作叫啥,株洲做网站定制,广西论坛网站建设,小制作小发明五年级张志成 诚迈科技高级技术专家 NAPI是什么 NAPI的概念源自Nodejs#xff0c;为了实现javascript脚本与C库之间的相互调用#xff0c;Nodejs对V8引擎的api做了一层封装#xff0c;称为NAPI。可以在Nodejs官网#xff08;https://nodejs.org/dist/latest-v20.x/docs/api/n-api… 张志成 诚迈科技高级技术专家 NAPI是什么 NAPI的概念源自Nodejs为了实现javascript脚本与C库之间的相互调用Nodejs对V8引擎的api做了一层封装称为NAPI。可以在Nodejs官网https://nodejs.org/dist/latest-v20.x/docs/api/n-api.html上查看各种NAPI接口定义说明。 可以看到NAPI接口本身是C语言实现的这些接口可以帮助C代码创建JS变量或访问JavaScript运行环境中的JS变量与方法。 OpenHarmony中的NAPI OpenAtom OpenHarmony以下简称“OpenHarmony”应用层基于javascript语言开发而系统框架层则基于C语言。它们之间需要一个桥梁来实现两种语言代码之间的相互调用这个桥梁就是NAPI。 这里可能有的小伙伴有疑问了OpenHarmony的NAPI和NodeJs的NAPI是一回事吗应该说OpenHarmony系统沿用了NAPI的接口定义形式但每个接口的内部实现都进行了重写。这是因为NAPI接口的本质是帮助C程序去跟Javascript引擎交互因此对于不同的引擎需要有不同的实现方式。当用户调用了NAPI接口 napi_create_int64() 对于Nodejs而言它会去访问V8引擎的api创建一个js的数字变量而对于OpenHarmony则是去访问ArkUI框架自己的js引擎(ArkNativeEngine)。在OpenHarmony源码中搜索 napi_create_int64() 方法你会得到一份头文件定义third_party\node\src\js_native_api.h以及两份不同的实现代码third_party\node\src\js_native_api_v8.ccfoundation\arkui\napi\native_engine\native_api.cppnative_api.cpp是OpenHarmony版本的NAPI实现想了解内部细节的可以从这里入手 创建一个简单的NAPI工程 可以通过DevEco Studio的Native C模板创建一个包含简单NAPI 实现的样例工程。 该工程自带一个hello.cpp实现了一个能够被javascript代码调用的add()方法。 下面我们就基于这个简单的例子探究一下NAPI框架的实现原理。 应用如何调用NAPI接口 应用代码导入对应的so库后就可以调用该库实现的接口。 这里我们注意到导入日志库时使用的名称是ohos.hilog应用代码如果写成 import hilog from libhilog.z.so 其实也是可以成功导入的。实际上ArkUI在运行时会将ohos.hilog转换为libhilog.z.so然后到 /system/lib/module/ 目录下查找此库并加载。系统实现的NAPI库都放在/system/lib/module/目录下类似的ohos.wifiManager对应的是 /system/lib/module/libwifimanager.z.soohos.deviceInfo 对应的是 /system/lib//module/libdeviceinfo.z.so 除了系统自带的NAPI库应用也可以用C开发自己的NAPI库。上面例子中 import testNapi from libentry.so 导入的就是应用自己实现的。应用开发的NAPI库会随着应用工程一起编译打包到hap文件中最终部署到/data目录每个应用自己的文件夹下。 NAPI库的导入原理 我们知道应用的javascript代码是由ArkUI的JS引擎解释执行的。当JS引擎解读 import hilog from ohos.hilog; 这行代码时会通过dlopen() 将对应的libhilog.z.so加载到应用进程中。这一切是怎么做到的呢每个应用进程在初始化时都会创建一个引擎实例 ArkNativeEngineImpl我们来看一下它的构造函数foundation\arkui\napi\native_engine\impl\ark\ark_native_engine_impl.cpp 也就是说每个应用进程的JS引擎中都注册了一个requireNapi函数当应用调用此方法时JS引擎就会通过NAPI框架的moduleManager类去处理so库的加载。moduleManager内部最终是找到了/system/lib/module下对应的so文件并通过dlopen()的方式加载到应用进程中。想了解细节的小伙伴可以读一下NativeModuleManager::LoadNativeModule()方法的内部实现。 这里可能会有个疑问应用的javascript代码中并没有写什么requireNapi的代码只有import xxx怎么触发的导入处理函数答案要到编译后的js代码中寻找。我们解开编译后的hap包找到ets文件对应的js文件 可以看到index.ets被编译成index.js后import关键字也被转为了requireNapi这样JS引擎在执行这行代码时就会去调用注册的导入处理函数了。 C库如何实现JS方法 前面解决了JS 导C库的问题下一步就是JS如何调用C库里的方法了。先说结论一个C方法能否被应用调用取决与C代码有没有将这个方法注册到JS引擎。 我们来看看hello.cpp是如何注册add方法的 我们可以从下往上看这段代码首先是 RegisterEntryModule(void) 方法。这是C向JS引擎进行NAPI模块与方法注册的起始代码。注意这个方法前面有个编译修饰符 __attribute__((constructor))它的作用是指导C代码的编译使得当so库被加载到应用进程中时RegisterEntryModule(void) 方法就会被自动调用到。该方法通过NAPI接口napi_module_register() 向JS引擎注册了一个 napi_module。 然后是Init()方法。该方法实现了Add方法的注册。也就是告诉JS引擎将JS符号add 与C方法Add 进行关联映射。这样后续当JS引擎解释执行javascript代码 testNapi.add(2, 3)时就会找到C Add()方法的函数地址并调用。如下图所示 方法关联调用的问题也解决了最后就是JS运行环境与C运行环境的相互切换了。当C的Add方法被JS引擎调用到后引擎会将javascript下发的参数变量传递给C。所有从JS运行环境传递过来的变量都是用napi_value类型来表示的。需要通过NAPI接口转为C语言的变量类型。详见下图每行代码的注释 napi_value不是一个具体的类型它类似于void*表示的是JS变量在JS引擎内部存储区内的地址。需要通过对应的NAPI方法实现例如napi_get_value_int32() --- js变量转为c整形napi_get_value_string_utf8() --- js变量转为c字符串napi_get_value_bool() --- js变量转为c布尔值 这些接口的具体用法和使用场景可以参考NodeJs官方文档https://nodejs.org/dist/latest-v20.x/docs/api/n-api.html C程序链接NAPI库 OpenHarmony的NAPI接口实现都封装在libace_napi.z.so中C程序编译时需链接此库。对于DevEco Studio应用开发的cpp代码在对应的CMakeLists.txt中链接。该库文件在SDK目录下可以找到。 对于设备侧开发系统框架中的C程序则通过BUILD.gn文件定义依赖关系。 总结 NAPI是JavaScript与C交互的桥梁。在OpenHarmony中Javascript代码在运行时由ArkUI的JS引擎解释执行C代码则通过NAPI接口访问JS引擎中的Javascript上下文从而实现与JS变量、方法之间的相互调用。 参考链接 以下是源码仓库地址 arkui_napi: Development framework for extending the JS Native Module | 原生模块扩展开发框架 third_party_node: Third-party open-source software node | 三方开源软件node