前言
此文代码版本为 code-v3.1-Beta。
2022/05/22 更新:oh 官方提供了一个插件用来一键生成 NAPI 框架代码、业务代码框架、GN 文件等。因此你也许没有必要看本文。
https://gitee.com/openharmony/napi_generator#https://gitee.com/openharmony/napi_generator/blob/master/docs/INSTRUCTION_ZH.md
“本文主要介绍NAPI框架代码生成工具,它可以根据用户指定路径下的ts(typescript)接口文件一键生成NAPI框架代码、业务代码框架、GN文件等。在开发JS应用与NAPI间接口时,底层框架开发者无需关注Nodejs语法、C++与JS之间的数据类型转换等上层应用转换逻辑,只关注底层业务逻辑即可,专业的人做专业的事,从而可以大大提高开发效率。”
NAPI 是什么
类似于 Android 中使用的 JNI,OH 中的 N-API 定义了的由 JS/ETS 语言编写的代码编绎出的字节码和 native 代码(使用 C/C++ 编写)交互的方式,由 Node.js N-API 框架扩展而来。
简单来说,OH 系统中的 NAPI 定义了 JS 代码和 C++ 代码的交互方式。
JNI:Java Native Interface;
N-API:Native Application Programming Interface;
Android 中使用的 JNI(Java Native Interface):定义了由 Java 或 Kotlin 语言编写的代码编译出的字节码和 native 代码(使用 C/C++ 编写)交互的方式。
NAPI 设计思想
1. 方舟编译器(ArkCompiler)将 JS 代码编绎成 abc 文件,并执行
方舟编译器(ArkCompiler)中的 ArkCompiler JS Runtime 模块中的 JS 编译工具链会将 JS 代码转换成方舟字节码即 abc 文件。然后方舟编译器中的 JS Runtime 会直接运动字节码文件,实现对应的语义逻辑。其中的 JS-NAPI 模块将实现 JSNAPI 接口和 C++ 的交互。
2. ArkCompiler 中的 JS-NAPI 模块调用 Native 代码注册的 JS 接口实现代码(callback method handle)
3. Native 代码以 property 的形式注册 callback method handle, 并且在 callback method 中有一个参数用于存放 JS 方法传入的参数值
如一个 js 对象对应的 native 对象 object,native 代码将把 object 中的方法、变量、常量构成 [key:methodname,value:callback method handle] 的 property 形式注册。
如:JS 的 call 接口 @ohos.telephony.call.d.ts 中对应的方法 dial()、变量 callstate、常量 CALL_STATE_IDLE,native 代码将为其注册 call 的 properties:["dial",callback method]、["callstate",getter/setter callback method]、["CALL_STATE_IDLE", callback value]
4. native 代码调用 napi 创建 JS 的 Promise,当 callback method 执行完成后,通过 napi 调用 Promise.resolve(value) 方法向 JS 代码返回结果。
NAPI 数据类型和封装函数
napi 数据类型作为 js 数据类型和 c++ 数据类型之间的桥梁,可以和 ArkComplier 中翻译的 js 数据类型,及 c++ 数据类型相互转换。napi 框架提供方法实现它们之间的互相转换。
可参考 Node.js v8.x 中文文档 ,oh 中的具体实现在 native_api.cpp。
其中基本的数据类型,如 :
- napi_env 对应 NativeEngine 在 oh 中指 ArkComplier 中 JSNAPI 的相关上下文环境,任何 napi 数据类型和 js 数据类型的转换都需要用到它。
- napi_value 对应 NativeValue 在 oh 中指所有 ArkComplier 可识别的 js 数据类型,它有子类 ArkNativeNumber、ArkNativeString、ArkNativeFunction 等,对应 js 中的 number、string、function 等数据类型。
- napi_callback_info 对应 NativeCallbackInfo ,指注册 callback handle 时用来存放 js 传入参数信息的数据类型。它是一个 struct。
- napi_property_descriptor 是用来存放单个 property 的数据类型。
- napi_callback 对应 NativeCallback,即前面的 callback handle,native 代码将其注册为对应 js 接口的回调函数。
typedef napi_value (*napi_callback)(napi_env env, napi_callback_info info)
其中基本的转换函数,如:
- 将 c++ 转换成 napi 的函数: napi_create_string_utf8 等;
- 将 napi 转换成 c++ 的函数: napi_get_value_string_utf8 等;
- 创建编译器中的 js 对象: napi_create_function 等;
- 回调 js 函数:napi_call_function
使用 NAPI
使用 napi 时只要完成以下几个步骤:
1. 向系统注册我们要实现的 js 接口对象(在 oh 中指要新增的子系统)
分为以下几步:
首先:
BUILD.gn 中声明 js 接口对象 object :ohos_shared_libarary("object")
其次:
通过 napi_module_register 注册 module
最后:
定义 napi_module ,注册 module 中 object 的 properties
2. 注册我们要提供的子系统接口的 properties
首先,定义所有的常量、变量、方法 property:
DECLARE_NAPI_FUNCTION("methodName", NativeMethod);
然后,注册所有 properties
通过 napi_define_properties 注册 object 的 properties
3. 实现所有 property 中的回调方法:
从 napi_callback_info 中取出 JS 调用时传入的所有参数,
并调用 napi 方法将其从 napi 数据类型的值 napi_value 转换成 native 数据类型的值