1# NAPI组件<a name="ZH-CN_TOPIC_0000001149901711"></a> 2 3- [简介](#section11660541593) 4- [目录](#section161941989596) 5- [使用场景](#section11759141594811) 6- [接口说明](#section1611515555510) 7- [开发步骤](#section937267212) 8- [相关仓](#section3970193518214) 9 10## 简介<a name="section11660541593"></a> 11 12NAPI(Native API)组件是一套对外接口基于Node.js N-API规范开发的原生模块扩展开发框架。 13 14**图 1** NAPI组件架构图<a name="fig1049423884819"></a> 15 16 17 18 19- **NativeEngine** 20 21 JS引擎抽象层,统一JS引擎在NAPI层的接口行为。 22 23- **ModuleManager** 24 25 管理模块,用于模块加载、模块信息缓存。 26 27- **ScopeManager** 28 29 管理NativeValue的生命周期。 30 31- **ReferenceManager** 32 33 管理NativeReference的生命周期。 34 35 36## 目录<a name="section161941989596"></a> 37 38NAPI组件源代码在/foundation/arkui/napi下,目录结构如下图所示: 39 40``` 41foundation/arkui/napi 42 ├── interfaces 43 │ └── kits 44 │ └── napi # NAPI头文件目录 45 ├── module_manager # 模块管理 46 ├── native_engine # NativeEngine抽象层 47 │ └── impl 48 │ └── ark # 基于Ark的NativeEngine实现 49 ├── scope_manager # 作用域管理 50 └── test # 测试目录 51``` 52 53## 使用场景<a name="section11759141594811"></a> 54 55NAPI适合封装IO、CPU密集型、OS底层等能力并对外暴露JS接口,通过NAPI可以实现JS与C/C++代码互相访问。我们可以通过NAPI接口构建例如网络通信、串口访问、多媒体解码、传感器数据收集等模块。 56 57## 接口说明<a name="section1611515555510"></a> 58 59接口实现详见:**foundation/arkui/napi**。 60 61**表 1** NAPI接口说明 62 63<a name="table10789351555"></a> 64<table><thead align="left"><tr id="row1878635175515"><th class="cellrowborder" valign="top" width="19.439999999999998%" id="mcps1.2.3.1.1"><p id="p27816352556"><a name="p27816352556"></a><a name="p27816352556"></a>接口分类</p> 65</th> 66<th class="cellrowborder" valign="top" width="80.56%" id="mcps1.2.3.1.2"><p id="p078835105514"><a name="p078835105514"></a><a name="p078835105514"></a>描述</p> 67</th> 68</tr> 69</thead> 70<tbody><tr id="row17863535520"><td class="cellrowborder" valign="top" width="19.439999999999998%" headers="mcps1.2.3.1.1 "><p id="p678143512552"><a name="p678143512552"></a><a name="p678143512552"></a>模块注册</p> 71</td> 72<td class="cellrowborder" valign="top" width="80.56%" headers="mcps1.2.3.1.2 "><p id="p4781035185515"><a name="p4781035185515"></a><a name="p4781035185515"></a>向模块管理注册模块信息的接口。</p> 73</td> 74</tr> 75<tr id="row67951538205618"><td class="cellrowborder" valign="top" width="19.439999999999998%" headers="mcps1.2.3.1.1 "><p id="p127961438195616"><a name="p127961438195616"></a><a name="p127961438195616"></a>异常&错误处理</p> 76</td> 77<td class="cellrowborder" valign="top" width="80.56%" headers="mcps1.2.3.1.2 "><p id="p47961338195617"><a name="p47961338195617"></a><a name="p47961338195617"></a>向JS抛出异常。</p> 78</td> 79</tr> 80<tr id="row778417510579"><td class="cellrowborder" valign="top" width="19.439999999999998%" headers="mcps1.2.3.1.1 "><p id="p1878575185715"><a name="p1878575185715"></a><a name="p1878575185715"></a>对象生命周期管理</p> 81</td> 82<td class="cellrowborder" valign="top" width="80.56%" headers="mcps1.2.3.1.2 "><p id="p1178565175711"><a name="p1178565175711"></a><a name="p1178565175711"></a>作用域管理,用于限定某个作用域范围内的NAPI对象的生命周期。</p> 83</td> 84</tr> 85<tr id="row83179619572"><td class="cellrowborder" valign="top" width="19.439999999999998%" headers="mcps1.2.3.1.1 "><p id="p1531786125717"><a name="p1531786125717"></a><a name="p1531786125717"></a>创建JS对象</p> 86</td> 87<td class="cellrowborder" valign="top" width="80.56%" headers="mcps1.2.3.1.2 "><p id="p12317663572"><a name="p12317663572"></a><a name="p12317663572"></a>创建标准的对象类型。</p> 88</td> 89</tr> 90<tr id="row63859616579"><td class="cellrowborder" valign="top" width="19.439999999999998%" headers="mcps1.2.3.1.1 "><p id="p438696165720"><a name="p438696165720"></a><a name="p438696165720"></a>C类型转NAPI类型</p> 91</td> 92<td class="cellrowborder" valign="top" width="80.56%" headers="mcps1.2.3.1.2 "><p id="p14386462570"><a name="p14386462570"></a><a name="p14386462570"></a>C到NAPI的类型转换。</p> 93</td> 94</tr> 95<tr id="row1145119612578"><td class="cellrowborder" valign="top" width="19.439999999999998%" headers="mcps1.2.3.1.1 "><p id="p13451116115716"><a name="p13451116115716"></a><a name="p13451116115716"></a>NAPI类型转C类型</p> 96</td> 97<td class="cellrowborder" valign="top" width="80.56%" headers="mcps1.2.3.1.2 "><p id="p12451464574"><a name="p12451464574"></a><a name="p12451464574"></a>NAPI到C的类型转换。</p> 98</td> 99</tr> 100<tr id="row1451436155720"><td class="cellrowborder" valign="top" width="19.439999999999998%" headers="mcps1.2.3.1.1 "><p id="p155147625713"><a name="p155147625713"></a><a name="p155147625713"></a>获取全局实例的函数</p> 101</td> 102<td class="cellrowborder" valign="top" width="80.56%" headers="mcps1.2.3.1.2 "><p id="p551466195713"><a name="p551466195713"></a><a name="p551466195713"></a>获取全局实例。</p> 103</td> 104</tr> 105<tr id="row65815617579"><td class="cellrowborder" valign="top" width="19.439999999999998%" headers="mcps1.2.3.1.1 "><p id="p458119617577"><a name="p458119617577"></a><a name="p458119617577"></a>JS值的操作</p> 106</td> 107<td class="cellrowborder" valign="top" width="80.56%" headers="mcps1.2.3.1.2 "><p id="p125818625711"><a name="p125818625711"></a><a name="p125818625711"></a>===、typeof、instanceof等操作符的NAPI接口。</p> 108</td> 109</tr> 110<tr id="row10649166145711"><td class="cellrowborder" valign="top" width="19.439999999999998%" headers="mcps1.2.3.1.1 "><p id="p18649196195716"><a name="p18649196195716"></a><a name="p18649196195716"></a>JS对象的属性操作</p> 111</td> 112<td class="cellrowborder" valign="top" width="80.56%" headers="mcps1.2.3.1.2 "><p id="p164919615574"><a name="p164919615574"></a><a name="p164919615574"></a>操作对象属性函数集。</p> 113</td> 114</tr> 115<tr id="row0714260574"><td class="cellrowborder" valign="top" width="19.439999999999998%" headers="mcps1.2.3.1.1 "><p id="p37143675714"><a name="p37143675714"></a><a name="p37143675714"></a>JS函数的操作</p> 116</td> 117<td class="cellrowborder" valign="top" width="80.56%" headers="mcps1.2.3.1.2 "><p id="p771466115711"><a name="p771466115711"></a><a name="p771466115711"></a>方法调用、实例创建。</p> 118</td> 119</tr> 120<tr id="row1578176155717"><td class="cellrowborder" valign="top" width="19.439999999999998%" headers="mcps1.2.3.1.1 "><p id="p1782126155711"><a name="p1782126155711"></a><a name="p1782126155711"></a>对象封装</p> 121</td> 122<td class="cellrowborder" valign="top" width="80.56%" headers="mcps1.2.3.1.2 "><p id="p87821967572"><a name="p87821967572"></a><a name="p87821967572"></a>绑定JS对象的外部上下文关系。</p> 123</td> 124</tr> 125<tr id="row8854116105717"><td class="cellrowborder" valign="top" width="19.439999999999998%" headers="mcps1.2.3.1.1 "><p id="p1685414619578"><a name="p1685414619578"></a><a name="p1685414619578"></a>简单异步</p> 126</td> 127<td class="cellrowborder" valign="top" width="80.56%" headers="mcps1.2.3.1.2 "><p id="p1685411612574"><a name="p1685411612574"></a><a name="p1685411612574"></a>创建异步任务。</p> 128</td> 129</tr> 130<tr id="row109154635718"><td class="cellrowborder" valign="top" width="19.439999999999998%" headers="mcps1.2.3.1.1 "><p id="p391556195720"><a name="p391556195720"></a><a name="p391556195720"></a>promise</p> 131</td> 132<td class="cellrowborder" valign="top" width="80.56%" headers="mcps1.2.3.1.2 "><p id="p1091566135717"><a name="p1091566135717"></a><a name="p1091566135717"></a>创建promise对象的函数集。</p> 133</td> 134</tr> 135<tr id="row1671493313016"><td class="cellrowborder" valign="top" width="19.439999999999998%" headers="mcps1.2.3.1.1 "><p id="p1971512331709"><a name="p1971512331709"></a><a name="p1971512331709"></a>脚本运行</p> 136</td> 137<td class="cellrowborder" valign="top" width="80.56%" headers="mcps1.2.3.1.2 "><p id="p371517332013"><a name="p371517332013"></a><a name="p371517332013"></a>运行JS代码。</p> 138</td> 139</tr> 140</tbody> 141</table> 142 143## 开发步骤<a name="section937267212"></a> 144 145下面以开发一个获取应用包名的JS接口为例介绍如何使用NAPI。 146 147我们要实现的JS接口原型是: 148 149``` 150function getAppName(): string; 151``` 152 153以下是实现源码: 154 155``` 156// app.cpp 157#include <stdio.h> 158#include <string.h> 159#include "napi/native_api.h" 160#include "napi/native_node_api.h" 161 162struct AsyncCallbackInfo { 163 napi_env env; 164 napi_async_work asyncWork; 165 napi_deferred deferred; 166}; 167 168// getAppName对应的C/C++实现函数 169napi_value JSGetAppName(napi_env env, napi_callback_info info) { 170 napi_deferred deferred; 171 napi_value promise; 172 // 创建promise 173 NAPI_CALL(env, napi_create_promise(env, &deferred, &promise)); 174 175 AsyncCallbackInfo* asyncCallbackInfo = new AsyncCallbackInfo { 176 .env = env, 177 .asyncWork = nullptr, 178 .deferred = deferred, 179 }; 180 181 napi_value resourceName; 182 napi_create_string_latin1(env, "GetAppName", NAPI_AUTO_LENGTH, &resourceName); 183 // 创建异步任务队列 184 napi_create_async_work( 185 env, nullptr, resourceName, 186 // 异步任务的回调 187 [](napi_env env, void* data) {}, 188 // 异步任务结束后的回调 189 [](napi_env env, napi_status status, void* data) { 190 AsyncCallbackInfo* asyncCallbackInfo = (AsyncCallbackInfo*)data; 191 napi_value appName; 192 const char* str = "com.example.helloworld"; 193 napi_create_string_utf8(env, str, strlen(str), &appName); 194 // 触发回调 195 napi_resolve_deferred(asyncCallbackInfo->env, asyncCallbackInfo->deferred, appName); 196 napi_delete_async_work(env, asyncCallbackInfo->asyncWork); 197 delete asyncCallbackInfo; 198 }, 199 (void*)asyncCallbackInfo, &asyncCallbackInfo->asyncWork); 200 napi_queue_async_work(env, asyncCallbackInfo->asyncWork); 201 return promise; 202} 203 204// 模块导出入口函数 205static napi_value AppExport(napi_env env, napi_value exports) 206{ 207 static napi_property_descriptor desc[] = { 208 DECLARE_NAPI_FUNCTION("getAppName", JSGetAppName), 209 }; 210 NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc)); 211 return exports; 212} 213 214// app模块描述 215static napi_module appModule = { 216 .nm_version = 1, 217 .nm_flags = 0, 218 .nm_filename = nullptr, 219 .nm_register_func = AppExport, 220 .nm_modname = "app", 221 .nm_priv = ((void*)0), 222 .reserved = {0} 223}; 224 225// 注册模块 226extern "C" __attribute__((constructor)) void AppRegister() 227{ 228 napi_module_register(&appModule); 229} 230``` 231 232对应编译脚本的实现: 233 234``` 235// BUILD.gn 236import("//build/ohos.gni") 237ohos_shared_library("app") { 238 # 指定编译源文件 239 sources = [ 240 "app.cpp", 241 ] 242 # 指定编译依赖 243 deps = [ "//foundation/arkui/napi:ace_napi" ] 244 # 指定库生成的路径 245 relative_install_dir = "module" 246 subsystem_name = "arkui" 247 part_name = "napi" 248} 249``` 250 251应用中的JS测试代码: 252 253``` 254import app from '@ohos.app' 255export default { 256 testGetAppName() { 257 app.getAppName().then(function (data) { 258 console.info('app name: ' + data); 259 }); 260 } 261} 262``` 263 264## 相关仓<a name="section3970193518214"></a> 265[ArkUI框架子系统](https://gitee.com/openharmony/docs/blob/master/zh-cn/readme/ArkUI%E6%A1%86%E6%9E%B6%E5%AD%90%E7%B3%BB%E7%BB%9F.md) 266 267[arkui\_ace\_engine](https://gitee.com/openharmony/arkui_ace_engine) 268 269[arkui\_ace\_engine\_lite](https://gitee.com/openharmony/arkui_ace_engine_lite) 270 271**arkui\_napi** 272 273