diff --git a/README.md b/README.md index 2eb28d142702d1e4d07281c3b13747857546e581..50a38a966d533ca828963d8e0e8dbdb0e2c0d17e 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # knowledge_demo_temp ## 简介 -该仓库用于临时存储各个场景的样例,当该场景样例达到一定数目之后即单独设置仓库承载。希望共建同学将自己的样例索引放置在合适的场景条目下。 +该仓库用于临时存储各个场景的样例,当该场景样例达到一定数目之后即单独设置仓库承载。希望共建同学将自己的样例索引放置在合适的场景条目下。 ## 运动健康场景 @@ -29,7 +29,7 @@ ### 样例汇总 -- [基于OpenHarmony的多功能手写板](./docs/writingPad/README.md) ++ [基于OpenHarmony的多功能手写板](./docs/writingPad/README.md) ## 学习资源 @@ -45,22 +45,22 @@ #### 轻量系统 -- [欧智通BES2600WM开发板轻量系统快速上手 ](docs/bes2600_hello_world) -- [润和Hi3861开发板轻量系统快速上手 ](docs/hi3861_hoperun_hello_world) -- [小熊派Hi3861开发板轻量系统快速上手 ](docs/hi3861_bearpi_hello_world) -- [开鸿智谷NiobeU4开发板轻量系统快速上手 ](docs/esp32_niobeu4_helloworld) ++ [欧智通BES2600WM开发板轻量系统快速上手](docs/bes2600_hello_world) ++ [润和Hi3861开发板轻量系统快速上手](docs/hi3861_hoperun_hello_world) ++ [小熊派Hi3861开发板轻量系统快速上手](docs/hi3861_bearpi_hello_world) ++ [开鸿智谷NiobeU4开发板轻量系统快速上手](docs/esp32_niobeu4_helloworld) #### 小型系统 -- [润和Hi3516DV300开发板小型系统快速上手 ](docs/hi3516_dv300_small_helloworld) -- [润和Hi3518EV300开发板小型系统快速上手 ](docs/hi3518_ev300_small_helloworld) ++ [润和Hi3516DV300开发板小型系统快速上手](docs/hi3516_dv300_small_helloworld) ++ [润和Hi3518EV300开发板小型系统快速上手](docs/hi3518_ev300_small_helloworld) #### 标准系统 -- [润和Hi3516DV300开发板标准系统快速上手 ](docs/hi3516_dv300_helloworld) -- [润和RK3568开发板标准系统快速上手 ](docs/rk3568_helloworld) -- [标准系统应用开发-HelloWorld ](docs/eTS_helloworld) -- [九联Unionpi Tiger开发板快速上手 ](docs/UnionpiTiger_helloworld) ++ [润和Hi3516DV300开发板标准系统快速上手](docs/hi3516_dv300_helloworld) ++ [润和RK3568开发板标准系统快速上手](docs/rk3568_helloworld) ++ [标准系统应用开发-HelloWorld](docs/eTS_helloworld) ++ [九联Unionpi Tiger开发板快速上手](docs/UnionpiTiger_helloworld) ## 内核Sample场景 @@ -72,13 +72,13 @@ #### 连接模组类应用 -- [任务调度_Thread](docs/hi3861_v100_demo_thread) -- [时间管理_Delay](docs/hi3861_v100_demo_delay) -- [时间管理_Timer](docs/hi3861_v100_demo_timer) -- [进程间通信_Mutex](docs/hi3861_v100_demo_mutex) -- [进程间通信_Semaphore](docs/hi3861_v100_demo_semaphore) -- [进程间通信_MessageQueue](docs/hi3861_v100_demo_messagequeue) -- [设备驱动_GPIO控制LED](docs/hi3861_v100_demo_gpio) ++ [任务调度_Thread](docs/hi3861_v100_demo_thread) ++ [时间管理_Delay](docs/hi3861_v100_demo_delay) ++ [时间管理_Timer](docs/hi3861_v100_demo_timer) ++ [进程间通信_Mutex](docs/hi3861_v100_demo_mutex) ++ [进程间通信_Semaphore](docs/hi3861_v100_demo_semaphore) ++ [进程间通信_MessageQueue](docs/hi3861_v100_demo_messagequeue) ++ [设备驱动_GPIO控制LED](docs/hi3861_v100_demo_gpio) ## 直播课程 @@ -90,14 +90,16 @@ #### 标准系统应用课程 -- [动画与音乐](docs/StandardCourse_AnimationAndMusic/readme.md) -- [NAPI](docs/StandardCourse_NAPI/README.md) ++ [动画与音乐](docs/StandardCourse_AnimationAndMusic/readme.md) ++ [NAPI](docs/StandardCourse_NAPI/README.md) ## 如何贡献 + [遵守本仓库的目录结构](https://gitee.com/openharmony-sig/knowledge/blob/master/docs/co-construct_demos/README_zh.md) + [遵守OpenHarmony编码贡献规范](https://gitee.com/openharmony-sig/knowledge_demo_smart_home/blob/master/dev/docs/contribute/README.md) + ## 参考资料 + + [OpenHarmony官网](https://www.openharmony.cn/) + [OpenHarmony知识体系仓库](https://gitee.com/openharmony-sig/knowledge) + [OpenHarmony知识体系议题申报](https://docs.qq.com/sheet/DUUNpcWR6alZkUmFO) diff --git "a/docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/ReadMe.md" b/docs/napi_study/ReadMe.md similarity index 100% rename from "docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/ReadMe.md" rename to docs/napi_study/ReadMe.md diff --git "a/docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/adapter_thirdlib_with_ide.md" b/docs/napi_study/docs/adapter_thirdlib_with_ide.md similarity index 91% rename from "docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/adapter_thirdlib_with_ide.md" rename to docs/napi_study/docs/adapter_thirdlib_with_ide.md index a746ca7faa548ba13d2af217708537b8eab951e4..f7fe64823b37624f65ef57f165e6ed4bca5aacbe 100755 --- "a/docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/adapter_thirdlib_with_ide.md" +++ b/docs/napi_study/docs/adapter_thirdlib_with_ide.md @@ -1,21 +1,33 @@ -# 如何通过IDE适配C/C++三方库并开发napi接口 +# 通过IDE集成C/C++三方库并开发napi接口 + ## 简介 -[如何贡献一个C/C++三方库](https://gitee.com/openharmony-sig/knowledge/blob/master/docs/openharmony_getstarted/port_thirdparty/README.md)一文介绍了我们如何适配一个Rom版的C/C++三方库,Rom版的三方库对于应用调用调不是很友好,尤其需要调试的时候,很不方便。因此本文将通过在IDE上适配openjpeg 三方库为例 介绍IDE上适配C/C++三方库的具体流程。 + +应用在调用系统固件集成的C/C++三方库时,可能会由于系统固件集成端与IDE的NDK中libc++版本不一致导致调用失败,而且系统固件集成的C/C++三方库对于应用的调式也很不友好,需要多方编译调试,很不方便。因此本文将通过在IDE上适配openjpeg 三方库为例介绍IDE上适配一个C/C++三方库的具体流程。 + ## 创建工程 + 在开发进行三方库适配以及napi接口开发前,我们需要创建一个三方库对应的工程,具体创建工程方法可以参照文档[通过IDE开发一个Napi工程](./hello_napi.md)。 + ## IDE上三方库的适配 -本文中我们已适配 openjpeg 为例介绍 整个适配流程 。 + +本文中我们以适配 openjpeg 为例介绍整个适配流程 。 + ### 原生库准备 + #### 下载代码 + 通过[openjpeg github网址](https://github.com/uclouvain/openjpeg/tree/v2.5.0),通过Code>>Download ZIP选项下载最新版本的源码包,并将其解压后放在IDE工程中的CPP目录下。 + - 下载openjpeg 2.5.0版本的库:
![download lib](media/download_lib.png) - 将库放在IDE工程:
 ![openjpeg localtion](media/lib_location.png) + #### 分析编译依赖 + - 分析CMakeLists.txt - 对于库不是很复杂,目录结构简单的库可以通过分析CMakeLists.txt文件来获取库的依赖。在openjpeg库中,
- 通过分析最外层CMakeLists.txt文件,可以得知编译openjpeg核心代码,需要依赖编译库自带thirdparty,如下图:
+ 对于库不是很复杂且目录结构简单的库可以通过分析CMakeLists.txt文件来获取库的依赖。在openjpeg库中, +通过分析最外层CMakeLists.txt文件,可以得知编译openjpeg核心代码以及需要依赖编译库自带thirdparty,如下图:
![Analyze Dependencies thirdparty](media/analysis_deps0.png) 而分析thirdparty/CMakeLists.txt文件,如果BUILD_THIRDPARTY开关未打开,将会通过FindPkgConfig自动搜索系统(IDE工具的SDK编译链)中的库文件,如下图:
 ![thirdparty](media/analysis_deps1.png)
@@ -27,6 +39,7 @@ - 通过运行cmake分析 我们除了分析CMakeLists.txt文件外,也可以通过在linux中通过cmake过程来分析对应的依赖。
我们在执行cmake的时候,会有对相关的依赖库的提示: + ```shell -- Large File support - found -- Looking for include file malloc.h @@ -50,14 +63,19 @@ -- Configuring done -- Generating done ``` - 由以上信息可以查看到,编译此库需要依赖libz(found version "1.2.11"),libpng(found version "1.6.37"),libtiff(found version "4.1.0")以及liblcms2(LCMS2 or LCMS lib not found, activate BUILD_THIRDPARTY if you want build it)。从提示中可以看到,如果想要使能对应的功能且系统库中包含该依赖的话,需要打开BUILD_THIRDPARTY 开关。
+ + 由以上信息可以查看到,编译此库需要依赖libz(found version "1.2.11"),libpng(found version "1.6.37"),libtiff(found version "4.1.0")以及liblcms2(LCMS2 or LCMS lib not found, activate BUILD_THIRDPARTY if you want build it)。从提示中可以看到,如果想要使能对应的功能且系统库中未包含该依赖的话,需要打开BUILD_THIRDPARTY 开关。
除了cmake中获取的依赖,我们也可以通过分析make编译过程获取更多的信息,具体方法可以参照[三方库适配依赖分析](https://gitee.com/openharmony-sig/knowledge/blob/master/docs/openharmony_getstarted/port_thirdparty/README.md#%E5%88%86%E6%9E%90%E7%BC%96%E8%AF%91%E4%BE%9D%E8%B5%96)。 + #### 重构CMakeLists + 对于不复杂的库且没有其他四方库依赖的三方库,我们可以使用原生库的CMakeLists.txt文件,但是对于一些复杂的,且强制依赖其他四方库的库,使用其原生CMakeLists.txt文件,无法解决四方库依赖的问题。此时,我们就需要对三方库的CMakeLists.txt文件进行重构。
重构CMakeLists文件主要需涉及以下几点: + - 动态生成配置文件 查看原生CMakeLists.txt文件中是否有configure_file配置项,如果没有可直接跳过此步骤。
在openjpeg库的原生CMakeLists.txt文件中,有2个配置文件需要动态生成:
+ ```cmake # opj_config.h generation (2/2) configure_file( @@ -72,10 +90,12 @@ @ONLY ) ``` + configure_file带了@ONLY参数,该参数限制了变量替换,让其只替换被@VAR@引用的变量。因此我们需要分析两个文件中对应的@VAR@配置规则。首先在opj_config.h.cmake.in文件定义的对应@VAR@的定义: + ```c++ /* create opj_config.h for CMake */ - #cmakedefine OPJ_HAVE_STDINT_H @OPJ_HAVE_STDINT_H@ + #cmakedefine OPJ_HAVE_STDINT_H @OPJ_HAVE_STDINT_H@ /*--------------------------------------------------------------------------*/ /* OpenJPEG Versioning */ @@ -84,7 +104,9 @@ #define OPJ_VERSION_MINOR @OPENJPEG_VERSION_MINOR@ #define OPJ_VERSION_BUILD @OPENJPEG_VERSION_BUILD@ ``` + 在分析@VAR@的赋值规则,在顶层CMakeLists.txt中: + ```cmake # opj_config.h generation (1/2) @@ -106,18 +128,24 @@ ensure_file_include("stdint.h" OPJ_HAVE_STDINT_H NO) ensure_file_include("inttypes.h" OPJ_HAVE_INTTYPES_H NO) ``` + 从以上内容通过分析ensure_file_include可以知道,在执行cmake时从系统中查找stdint.h文件,如果查找到则将OPJ_HAVE_STDINT_H置起,否则不会改变。而IDE上是没有stdint.h文件的,因此我们需要在配置文件之前手动将OPJ_HAVE_STDINT_H置起,如下: + ```cmake set(OPJ_HAVE_STDINT_H TRUE) ``` + 其他变量也是通过类似的方法分析。 - 相关宏定义的设置 通过add_definitions设置三方库的相关宏定义,如下所示: + ```cmake add_definitions(-DOPJ_HAVE_PNG_H=0 -DOPJ_HAVE_TIFF_H=1 -DOPJ_HAVE_LCMS2_H=1) ``` + - 源码文件编译 三方库编译的源码可以通过在linux执行make的时候查看分析到: + ```shell [ 0%] Building C object src/lib/openjp2/CMakeFiles/openjp2_static.dir/thread.c.o [ 1%] Building C object src/lib/openjp2/CMakeFiles/openjp2_static.dir/bio.c.o @@ -291,7 +319,9 @@ [100%] Linking C executable ../../../bin/opj_decompress [100%] Built target opj_decompress ``` + 由上面过程可看出,每个编译目标(Built target)所依赖的文件由上一个目标(如果上一个目标由的话)生成后以及该目标生成前的文件组成,如生成libopenjp2.so(Built target openjp)的文件是Built target openjp2_static之后以及Built target openjp之前的文件: + ```shell [ 14%] Building C object src/lib/openjp2/CMakeFiles/openjp2.dir/thread.c.o [ 15%] Building C object src/lib/openjp2/CMakeFiles/openjp2.dir/bio.c.o @@ -316,7 +346,9 @@ [ 27%] Building C object src/lib/openjp2/CMakeFiles/openjp2.dir/opj_malloc.c.o [ 28%] Building C object src/lib/openjp2/CMakeFiles/openjp2.dir/sparse_array.c.o ``` + 因此生成libopenjpeg.so的源文件我们就可以这样写: + ```cmake set(SHARED_LIB_SRC "${CMAKE_CURRENT_SOURCE_DIR}/src/lib/openjp2/thread.c" "${CMAKE_CURRENT_SOURCE_DIR}/src/lib/openjp2/bio.c" @@ -341,18 +373,24 @@ "${CMAKE_CURRENT_SOURCE_DIR}/src/lib/openjp2/opj_malloc.c" "${CMAKE_CURRENT_SOURCE_DIR}/src/lib/openjp2/sparse_array.c") ``` + 其中CMAKE_CURRENT_SOURCE_DIR是代表当前CMakeLists.txt文件所在的路径。 - 目标文件生成 这里我们调用add_library生成一个目标文件libopenjpeg_shared.so,其依赖上面分析的源码share_lib_src: + ```cmake add_library(openjp2 SHARED ${SHARED_LIB_SRC}) ``` + - 添加其他四方库依赖 如果三方库有对其他四方库依赖的话,需要通过target_link_libraries()方法添加对应的依赖。如在工程CPP目录下的CMakeLists.txt文件中的: + ```cmake target_link_libraries(entry PUBLIC libace_napi.z.so openjg2) # libace_napi.z.so-系统napi库, openjg2-依赖的openjpeg库 ``` + - 完整的CMakeLists.txt + ```cmake # the minimum version of CMake. cmake_minimum_required(VERSION 3.4.1) @@ -409,55 +447,69 @@ add_library(openjp2 SHARED ${SHARED_LIB_SRC}) target_include_directories(openjp2 PRIVATE ${INCLUDE_DIR}) ``` + ### 加入编译构建 -原生库源码准备完后,我们需要将库加入到工程的编译构建中。在工程目录CPP下的CMakeLists.txt文件中,通过add_subdirectory将openjpeg加入到编译中,并通过target_link_libraries添加对openjpeg的链接,如下图: -![引入三方库编译](media/add_lib.png) + +原生库源码准备完后,我们需要将库加入到工程的编译构建中。在工程目录CPP下的CMakeLists.txt文件中,通过add_subdirectory将openjpeg加入到编译中,并通过target_link_libraries添加对openjpeg的链接,如下图:
+![引入三方库编译](media/add_lib.png)
特别说明:
如果使用原生库的CMakeLists文件,编译时会提示添加目标时需要重新更改目标路径:
 ![errorfor repath](media/err_repath.png) 此时我们就需要在顶层目录的CMakeLists.txt文件中添加 -``` + +```cmake set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) ``` + ![add repath config](media/add_repath.png) 到此,我们的三方库适配已经完成并可以正常编译成功。 + ## 三方库napi接口开发 + 三方库napi的接口一般是由需求方提供的,对于无需求或需要自己定义接口的,我们可以根据三方库对外导出的API接口进行封装或是根据原生库的测试用例对外封装测试接口。本文中我们已封装2个openjpeg测试接口为例详细说明napi接口开发的具体流程。 + ### 定义napi接口 + 根据原生库的测试用例,我们封装2个测试用例接口 + ```c++ typedef struct { - int comps_num; // the number of components of the image. - int comps_prec; // number of bits per component per pixel - int img_width; // the image width - int img_height; // the image height - int title_width; // width of tile - int title_height; // height of title - int irreversible; // 1 : use the irreversible DWT 9-7 - // 0 : use lossless compression (default) - int cblockw_init; // initial code block width, default to 64 - int cblockh_init; // initial code block height, default to 64 - int numresolution; // number of resolutions - int offsetx; // x component offset compared to the whole image - int offsety; // y component offset compared to the whole image - int is_rand; // Whether to generate data randomly - char file[256]; // output filename + int comps_num; // the number of components of the image. + int comps_prec; // number of bits per component per pixel + int img_width; // the image width + int img_height; // the image height + int title_width; // width of tile + int title_height; // height of title + int irreversible; // 1 : use the irreversible DWT 9-7 + // 0 : use lossless compression (default) + int cblockw_init; // initial code block width, default to 64 + int cblockh_init; // initial code block height, default to 64 + int numresolution; // number of resolutions + int offsetx; // x component offset compared to the whole image + int offsety; // y component offset compared to the whole image + int is_rand; // Whether to generate data randomly + char file[256]; // output filename } J2K_Info; -int OpenjpegCompress(const char *input_file, char *output_file) # 图片压缩成J2K格式 -int openjpeg_create_j2k(J2K_Info *info) # 创建一张J2K格式图片 +int OpenjpegCompress(const char *input_file, char *output_file) # 图片压缩成J2K格式 +int openjpeg_create_j2k(J2K_Info *info) # 创建一张J2K格式图片 ``` + ### 注册接口 + ```c++ napi_property_descriptor desc[] = { {"openjpeg_compress", nullptr, OpenjpegCompress, nullptr, nullptr, - nullptr, napi_default, nullptr}, + nullptr, napi_default, nullptr}, {"openjpeg_create_j2k", nullptr, OpenjpegCreateJ2K , nullptr, nullptr, - nullptr, napi_default, nullptr} + nullptr, napi_default, nullptr} }; ``` + ### 接口实现 + - openjpeg_compress接口的实现 + ```c++ static napi_value OpenjpegCompressMethod(napi_env env, napi_callback_info info) { @@ -489,7 +541,7 @@ napi_property_descriptor desc[] = { if (OpenjpegCompress(input_file, output_file) != 0) { return result; } - // 创建返回的js类型参数 + // 创建返回的js类型参数 if (napi_create_int64(env, 0, &result) != napi_ok) { std::cout << "napi_create_int64" << std::endl; } @@ -497,16 +549,18 @@ napi_property_descriptor desc[] = { return result; } ``` + - openjpeg_create_j2k接口的实现 + ```c++ static napi_value OpenjpegCreateJ2K(napi_env env, napi_callback_info info) { - napi_value result = nullptr; + napi_value result = nullptr; napi_get_undefined(env, &result); napi_value value; size_t argc = 1; J2K_Info j2kInfo; - // 获取参数 + // 获取参数 if (napi_get_cb_info(env, info, &argc, &value, nullptr, nullptr) != napi_ok) { return result; } @@ -527,11 +581,13 @@ napi_property_descriptor desc[] = { return result; } ``` + 解析参数接口的实现: + ```c++ static int OpenjpegGetJ2kInfo(napi_env env, napi_value value, J2K_Info *info) { - if (info == nullptr) { + if (info == nullptr) { return -1; } if(GetObjectPropetry(env, value,"output_file", STRING, info->file) != napi_ok) { @@ -571,14 +627,16 @@ napi_property_descriptor desc[] = { return 0; } ``` + 由上代码可以看出,OpenjpegGetJ2kInfo接扣调用了一个封装的接口GetObjectPropetry,该接口实现了通过调用napi的接口获取对应的数据: + ```c++ static int GetObjectPropetry(napi_env env, napi_value object, std::string key, int keyType, void *retValue) { napi_value property = nullptr; napi_value result = nullptr; bool flag = false; int ret = -1; - // 通过字符串获取napi_value对象 + // 通过字符串获取napi_value对象 if (napi_create_string_utf8(env, key.c_str(), strlen(key.c_str()), &property) != napi_ok) { return ret; @@ -622,9 +680,12 @@ napi_property_descriptor desc[] = { return 0; } ``` + ## 应用调用napi接口 + - 应用申明接口 在确定需要封装的接口后,我们需要将这些接口定义在index.d.ts文件中(路径entry/src/main/cpp/types/libentry/index.d.ts) + ```js export const openjpeg_compress: (srcName:string, desName:string) =>number; interface openjpegOption{ @@ -635,7 +696,7 @@ napi_property_descriptor desc[] = { title_width:number // width of tile title_height:number // height of title irreversible:number // 1 : use the irreversible DWT 9-7, - // 0 : use lossless compression (default) + // 0 : use lossless compression (default) output_file:string // output filename cblockw_init?:number // initial code block width, default to 64 cblockh_init?:number // initial code block height, default to 64 @@ -646,8 +707,10 @@ napi_property_descriptor desc[] = { } export const openjpeg_create_j2k: (option:openjpegOption) => number ``` + - 应用调用接口 在ets工程中创建2个按钮,并通过按钮调用相关的接口,具体代码如下: + ```js Button(this.buttonTxt0) .fontSize(50) @@ -668,8 +731,10 @@ napi_property_descriptor desc[] = { "newImage.j2k"}) }) ``` + ## 参考资料 -- [如何通过DevEco Studio开发一个NAPI工程]()。 + +- [如何通过DevEco Studio开发一个NAPI工程](./adapter_thirdlib_with_ide.md)。 - [如何贡献一个C/C++三方库](https://gitee.com/openharmony-sig/knowledge/blob/master/docs/openharmony_getstarted/port_thirdparty/README.md)。 -- [IDE适配三方库Openjpeg源码](../source/openjpeg/)。 +- [IDE集成三方库Openjpeg源码](../source/openjpeg/)。 - [OpenHarmony 知识体系](https://gitee.com/openharmony-sig/knowledge/tree/master)。 diff --git "a/docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/hello_napi.md" b/docs/napi_study/docs/hello_napi.md similarity index 95% rename from "docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/hello_napi.md" rename to docs/napi_study/docs/hello_napi.md index a72bf0f4d2a8af44ad8a2daf38e54c89291ba10c..79842f04c887157a71706fb9f4dae2086dd63316 100755 --- "a/docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/hello_napi.md" +++ b/docs/napi_study/docs/hello_napi.md @@ -2,14 +2,14 @@ ## 简介 -NAPI(Native API)是OpenHarmony系统中的一套原生模块扩展开发框架,它基于Node.js N-API规范开发,为开发者提供了JavaScript与C/C++模块之间相互调用的交互能力。如下图所示: +NAPI(Native API)是OpenHarmony系统中的一套原生模块扩展开发框架,它基于Node.js N-API规范开发,为开发者提供了JavaScript与C/C++模块之间相互调用的交互能力。如下图所示:
![napi framework](media/napi_framework.png)
这套机制对于鸿蒙系统开发的价值有两方面: - OpenHarmony系统系统可以将框架层丰富的模块功能通过js接口开放给上层应用使用。 - 应用开发者也可以选择将一些对性能、底层系统调用有要求的核心功能用C/C++封装实现,再通过js接口使用,提高应用本身的执行效率。 -本文将通过一个Hello world的实例来演示如何在DevEco Studio上开发一个NAPI工程的过程。如何开发一个rom包的napi工程可以参考文档[深入浅出 OpenHarmony NAPI](https://gitee.com/javen678/hello-ohos-napi/blob/master/doc/README.md); +本文将通过一个Hello world的实例来演示如何在DevEco Studio上开发一个NAPI工程的过程。 ## 工程准备 diff --git "a/docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/NAPI-SyncWorkFlow.png" b/docs/napi_study/docs/media/NAPI-SyncWorkFlow.png similarity index 100% rename from "docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/NAPI-SyncWorkFlow.png" rename to docs/napi_study/docs/media/NAPI-SyncWorkFlow.png diff --git "a/docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/NAPI-aSyncWorkFlow.png" b/docs/napi_study/docs/media/NAPI-aSyncWorkFlow.png similarity index 100% rename from "docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/NAPI-aSyncWorkFlow.png" rename to docs/napi_study/docs/media/NAPI-aSyncWorkFlow.png diff --git "a/docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/NativeEngine.jpg" b/docs/napi_study/docs/media/NativeEngine.jpg similarity index 100% rename from "docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/NativeEngine.jpg" rename to docs/napi_study/docs/media/NativeEngine.jpg diff --git a/docs/napi_study/docs/media/add_lib.png b/docs/napi_study/docs/media/add_lib.png new file mode 100755 index 0000000000000000000000000000000000000000..4561475112aa97511f4e633ec5f6fe87ebff281d Binary files /dev/null and b/docs/napi_study/docs/media/add_lib.png differ diff --git a/docs/napi_study/docs/media/add_repath.png b/docs/napi_study/docs/media/add_repath.png new file mode 100755 index 0000000000000000000000000000000000000000..4120c0fe93eeb6550147378c794e346be9f64b1b Binary files /dev/null and b/docs/napi_study/docs/media/add_repath.png differ diff --git "a/docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/analysis_deps0.png" b/docs/napi_study/docs/media/analysis_deps0.png similarity index 100% rename from "docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/analysis_deps0.png" rename to docs/napi_study/docs/media/analysis_deps0.png diff --git "a/docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/analysis_deps1.png" b/docs/napi_study/docs/media/analysis_deps1.png similarity index 100% rename from "docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/analysis_deps1.png" rename to docs/napi_study/docs/media/analysis_deps1.png diff --git "a/docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/auto_signature.png" b/docs/napi_study/docs/media/auto_signature.png similarity index 100% rename from "docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/auto_signature.png" rename to docs/napi_study/docs/media/auto_signature.png diff --git "a/docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/confi_file.png" b/docs/napi_study/docs/media/confi_file.png similarity index 100% rename from "docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/confi_file.png" rename to docs/napi_study/docs/media/confi_file.png diff --git "a/docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/config.png" b/docs/napi_study/docs/media/config.png similarity index 100% rename from "docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/config.png" rename to docs/napi_study/docs/media/config.png diff --git "a/docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/config_project.png" b/docs/napi_study/docs/media/config_project.png similarity index 100% rename from "docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/config_project.png" rename to docs/napi_study/docs/media/config_project.png diff --git "a/docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/config_var.png" b/docs/napi_study/docs/media/config_var.png similarity index 100% rename from "docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/config_var.png" rename to docs/napi_study/docs/media/config_var.png diff --git "a/docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/create_project.png" b/docs/napi_study/docs/media/create_project.png similarity index 100% rename from "docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/create_project.png" rename to docs/napi_study/docs/media/create_project.png diff --git "a/docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/device.png" b/docs/napi_study/docs/media/device.png similarity index 100% rename from "docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/device.png" rename to docs/napi_study/docs/media/device.png diff --git a/docs/napi_study/docs/media/download_lib.png b/docs/napi_study/docs/media/download_lib.png new file mode 100755 index 0000000000000000000000000000000000000000..040e5cbae1abed6699fc994d27cc5b331b9bf800 Binary files /dev/null and b/docs/napi_study/docs/media/download_lib.png differ diff --git "a/docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/err_repath.png" b/docs/napi_study/docs/media/err_repath.png similarity index 100% rename from "docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/err_repath.png" rename to docs/napi_study/docs/media/err_repath.png diff --git a/docs/napi_study/docs/media/export_class.png b/docs/napi_study/docs/media/export_class.png new file mode 100755 index 0000000000000000000000000000000000000000..5a9474123bd40ba666bc20fc72b2317596e844e2 Binary files /dev/null and b/docs/napi_study/docs/media/export_class.png differ diff --git "a/docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/ide_config1.jpg" b/docs/napi_study/docs/media/ide_config1.jpg similarity index 100% rename from "docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/ide_config1.jpg" rename to docs/napi_study/docs/media/ide_config1.jpg diff --git "a/docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/ide_config2.jpg" b/docs/napi_study/docs/media/ide_config2.jpg similarity index 100% rename from "docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/ide_config2.jpg" rename to docs/napi_study/docs/media/ide_config2.jpg diff --git "a/docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/ide_config3.jpg" b/docs/napi_study/docs/media/ide_config3.jpg similarity index 100% rename from "docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/ide_config3.jpg" rename to docs/napi_study/docs/media/ide_config3.jpg diff --git "a/docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/ide_config4.jpg" b/docs/napi_study/docs/media/ide_config4.jpg similarity index 100% rename from "docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/ide_config4.jpg" rename to docs/napi_study/docs/media/ide_config4.jpg diff --git "a/docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/ide_config5.jpg" b/docs/napi_study/docs/media/ide_config5.jpg similarity index 100% rename from "docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/ide_config5.jpg" rename to docs/napi_study/docs/media/ide_config5.jpg diff --git a/docs/napi_study/docs/media/lib_location.png b/docs/napi_study/docs/media/lib_location.png new file mode 100755 index 0000000000000000000000000000000000000000..6e2ed6dc0af3f6932700803e1b0566ba3fde7d2f Binary files /dev/null and b/docs/napi_study/docs/media/lib_location.png differ diff --git "a/docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/libpng_analysis.png" b/docs/napi_study/docs/media/libpng_analysis.png similarity index 100% rename from "docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/libpng_analysis.png" rename to docs/napi_study/docs/media/libpng_analysis.png diff --git a/docs/napi_study/docs/media/life_cycle.png b/docs/napi_study/docs/media/life_cycle.png new file mode 100755 index 0000000000000000000000000000000000000000..24d7de926fde2114ae84e676349361e4562adf0d Binary files /dev/null and b/docs/napi_study/docs/media/life_cycle.png differ diff --git "a/docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/log.png" b/docs/napi_study/docs/media/log.png similarity index 100% rename from "docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/log.png" rename to docs/napi_study/docs/media/log.png diff --git "a/docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/module_manager_h.jpg" b/docs/napi_study/docs/media/module_manager_h.jpg similarity index 100% rename from "docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/module_manager_h.jpg" rename to docs/napi_study/docs/media/module_manager_h.jpg diff --git "a/docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/napi_framework.png" b/docs/napi_study/docs/media/napi_framework.png similarity index 100% rename from "docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/napi_framework.png" rename to docs/napi_study/docs/media/napi_framework.png diff --git "a/docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/napi_module.png" b/docs/napi_study/docs/media/napi_module.png similarity index 100% rename from "docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/napi_module.png" rename to docs/napi_study/docs/media/napi_module.png diff --git "a/docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/run.png" b/docs/napi_study/docs/media/run.png similarity index 100% rename from "docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/run.png" rename to docs/napi_study/docs/media/run.png diff --git "a/docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/set_thirdparty.png" b/docs/napi_study/docs/media/set_thirdparty.png similarity index 100% rename from "docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/set_thirdparty.png" rename to docs/napi_study/docs/media/set_thirdparty.png diff --git "a/docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/signing_ok.png" b/docs/napi_study/docs/media/signing_ok.png similarity index 100% rename from "docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/signing_ok.png" rename to docs/napi_study/docs/media/signing_ok.png diff --git "a/docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/ui_framework.png" b/docs/napi_study/docs/media/ui_framework.png similarity index 100% rename from "docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/ui_framework.png" rename to docs/napi_study/docs/media/ui_framework.png diff --git "a/docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/napi_asynchronous_call.md" b/docs/napi_study/docs/napi_asynchronous_call.md similarity index 96% rename from "docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/napi_asynchronous_call.md" rename to docs/napi_study/docs/napi_asynchronous_call.md index d4c75dfffe18c9b9a624845a098b4daac18eb0aa..bde0d837342a9052de55e0111781afe807d03f01 100755 --- "a/docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/napi_asynchronous_call.md" +++ b/docs/napi_study/docs/napi_asynchronous_call.md @@ -1,12 +1,17 @@ # NAPI异步调用 + ## 简介 + OpenHarmony Napi 标准系统异步接口实现支持Callback方式和Promise方式。标准系统异步接口实现规范要求,若引擎开启Promise特性支持,则异步方法必须同时支持Callback方式和Promise方式。使用哪种方式由应用开发者决定,通过是否传递Callback函数进行区分。不传递Callback即为Promise方式,方法执行结果为Promise实例对象。 + ## 异步方式实现原理 + - 异步方式原理 同步方式,所有的代码处理都在原生方法(主线程)中完成。异步方式依赖NAPI框架提供的napi_create_async_work()函数创建异步工作项,原生方法被调用时,原生方法完成数据接收、转换,存入上下文数据,之后创建一个异步工作项,并加入调度队列,由异步工作线程池统一调度,原生方法返回空值(Callback方式)或返回Promise对象(Promise方式)。异步工作项中定义了2个函数,一个用于执行工作项的业务逻辑,异步工作项被调度后,该函数从上下文数据中获取输入数据,在worker线程中完成业务逻辑计算(不阻塞主线程)并将结果写入上下文数据。业务逻辑处理函数执行完成或被取消后,触发EventLoop执行另一函数,函数从上下文数据中获取结果,转换为JS类型,调用JS回调函数或通过Promise resolve()返回结果。 - 异步方式处理流程图 ![同步调用实现方法的代码处理流程](media/NAPI-aSyncWorkFlow.png) - napi_create_async_work() + ```c++ napi_status napi_create_async_work(napi_env env, napi_value async_resource, @@ -16,6 +21,7 @@ OpenHarmony Napi 标准系统异步接口实现支持Callback方式和Promise方 void* data, napi_async_work* result); ``` + 参数说明: [in] env: 传入接口调用者的环境,包含js引擎等,由框架提供,默认情况下直接传入即可。
[in] async_resource: 可选项,关联async_hooks。
@@ -23,14 +29,20 @@ OpenHarmony Napi 标准系统异步接口实现支持Callback方式和Promise方 [in] execute: 执行业务逻辑计算函数,由worker线程池调度执行。在该函数中执行IO、CPU密集型任务,不阻塞主线程。
[in] complete: execute参数指定的函数执行完成或取消后,触发执行该函数。此函数在EventLoop线程中执行。
[in] data: 用户提供的上下文数据,用于传递数据。
- [out] result: napi_async_work*指针,用于返回当前此处函数调用创建的异步工作项。 返回值:返回napi_ok表示转换成功,其他值失败。
+ [out] result: napi_async_work*指针,用于返回当前此处函数调用创建的异步工作项。返回值:返回napi_ok表示转换成功,其他值失败。
+ ## Callback 异步接口 + 下面基于napi_create_async_work将add()接口改成Callback方式接口——addCallback(),接口的eTS定义 -```c++ + +```ts function addAsyncCallback(numX: number, numY: number, callback:(result: number) => void): void; ``` - ### 初始化上下文数据 + +### 初始化上下文数据 + 根据业务需求自定义一个上下文数据结构,用于保存和传递数据。本例自定义的上下文数据包含:异步工作项对象、回调函数、2个参数(加数、被加数)、计算结果等4个属性。 + ```c++ struct AddonData { napi_async_work asyncWork = nullptr; @@ -39,8 +51,10 @@ struct AddonData { double result = nullptr; }; ``` + 在[napi数据类型](./napi_data_type.md)文中,我们已了解对于NAPI框架,所有参数,无论是ECMAScript标准中定义的Boolean、Null、Undefined、Number、BigInt、String、Symbol和Object八种数据类型,还是Function类型,都已统一封装为napi_value类型,故可如获取数据类型的参数一样获取Function类型参数,本例直接调用函数获取3个参数——加数、被加数、回调函数。
接着我们将接收到的参数转换存入上下文数据,number类型的转换为double直接存入即可。Function类型的参数怎么处理?不转换直接存入napi_value类型?答案是不行的!这牵涉到NAPI对象生命周期管理问题。napi_value类型引用对象的生命周期在原生方法退出后结束,后面在work线程无法获取其值。NAPI提供了一种生命期限长于原生方法的对象引用类型—— napi_ref,napi_ref引用对象在原生方法退出后不自动回收,由用户管理此类型对象的生命周期。所以当前方法中,我们调用napi_create_reference()函数将接收到的napi_value类型的回调函数参数args[2]转换为napi_ref类型(生命周期具体定义及使用可参照文档[napi生命周期](./napi_life_cycle.md))。 + ```c++ static napi_value addAsyncCallback(napi_env env, napi_callback_info info) { // 获取3个参数,值的类型是js类型(napi_value) @@ -61,8 +75,11 @@ static napi_value addAsyncCallback(napi_env env, napi_callback_info info) { ... } ``` + ### 创建异步工作项 + 在创建异步工作项前,我们先分别声明2个函数,分别用作于napi_create_async_work()函数的execute、complete参数。异步工作项创建OK后,将其存入上下文数据的asyncWork属性,并调用napi_queue_async_work()将异步工作项加入调度队列,由异步work线程池统一调度,原生方法返回空值退出。 + ```c++ // 业务逻辑处理函数,由worker线程池调度执行。 static void addExecuteCB(napi_env env, void *data) { @@ -90,8 +107,11 @@ static napi_value addAsyncCallback(napi_env env, napi_callback_info info) { return result; } ``` + ### execute 函数 + execute函数在异步工作项被调度后在work线程中执行,不阻塞主线程(不阻塞UI界面),可执行IO、CPU密集型等任务。此处仅为演示,我们的业务逻辑计算就是一个简单的加法,并把计算结果存入上下文数据的result属性。 + ```c++ // 业务逻辑处理函数,由worker线程池调度执行。 static void addExecuteCB(napi_env env, void *data) { @@ -101,8 +121,11 @@ static void addExecuteCB(napi_env env, void *data) { addonData->result = addonData->args[0] + addonData->args[1]; } ``` + ### complete 函数 + 从接收到的上下文数据中获取结果,调用napi_call_function()方法执行JS回调函数返回数据给JS。之后释放过程中创建的napi_ref引用对象、异步工作项等对象。 NAPI框架提供了napi_call_function()函数供扩展Natvie代码(C/C++代码)调用JS函数,用于执行回调函数等场景。函数定义如下: + ```c++ NAPI_EXTERN napi_status napi_call_function(napi_env env, napi_value recv, @@ -111,15 +134,19 @@ NAPI_EXTERN napi_status napi_call_function(napi_env env, const napi_value* argv, napi_value* result); ``` + 参数说明: + - [in] env: 传入接口调用者的环境,包含js引擎等,由框架提供,默认情况下直接传入即可。 - [in] recv: 传给被调用的this对象。 - [in] func: 被调用的函数. - [in] argc: 函数参数个数(对应函数数组的长度)。 - [in] argv: 函数参数数组. - [out] result: func函数执行的返回值。 返回值:返回napi_ok表示转换成功,其他值失败。 -因对象生命周期管理问题,上下文数据的callback属性的类型为napi_ref,需要调用napi_get_reference_value()函数获取其指向的napi_value对象值才调用napi_call_function()函数。napi_get_reference_value()函数介绍参照文档[napi生命周期](./napi_life_cycle.md)。
+因对象生命周期管理问题,上下文数据的callback属性的类型为napi_ref,需要调用napi_get_reference_value()函数获取其指向的napi_value对象值才调用napi_call_function()函数。napi_get_reference_value()函数介绍参照文档[napi生命周期](./napi_life_cycle.md)。 + complete接口实现: + ```c++ // 业务逻辑处理完成回调函数,在业务逻辑处理函数执行完成或取消后触发,由EventLoop线程中执行。 static void addAsyncCompleteCB(napi_env env, napi_status status, void *data) { @@ -140,7 +167,9 @@ static void addAsyncCompleteCB(napi_env env, napi_status status, void *data) { delete loginAddonData; } ``` -### eTS调用接口 + +### eTS调用接口 + ```js import testNapi from "libentry.so"; @@ -165,20 +194,28 @@ struct Index { .height('100%') } ``` + ## Promise 接口 + ### 创建Promise + 通过前面异步方式实现原理我们可知Promise整体处理流程和Callback方式一样。不同的是,首先要创建一个Promise。NAPI框架中提供了napi_create_promise()函数用于创建Promise,调用该函数输出2个对象——deferred、promise。promise用于原生方法返回,deferred传入异步工作项的上下文数据。complete函数中,应用napi_resolve_deferred()函数 或 napi_reject_deferred() 函数返回数据。
函数定义如下: + ```c++ napi_status napi_create_promise(napi_env env, napi_deferred* deferred, napi_value* promise); ``` + 参数说明: + - [in] env: 传入接口调用者的环境,包含js引擎等,由框架提供,默认情况下直接传入即可。 - [out] deferred: 返回接收刚创建的deferred对象,关联Promise对象,后面使用napi_resolve_deferred() 或 napi_reject_deferred() 返回数据。 - [out] promise: 关联上面deferred对象的JS Promise对象 返回值:返回napi_ok表示转换成功,其他值失败。 + 创建Promise接口的实现: + ```c++ static napi_value addPromise(napi_env env, napi_callback_info info) { // 创建promise @@ -192,8 +229,11 @@ static napi_value addPromise(napi_env env, napi_callback_info info) { return promise; } ``` + ### 初始化上下文数据 -同Callback方式定义一个上下文数据结构,用于保存和传递数据。Promise方式去掉callback属性,加上deferred属性。 + +同Callback方式定义一个上下文数据结构,用于保存和传递数据。Promise方式去掉callback属性,加上deferred属性。 + ```c++ // 用户提供的上下文数据,在原生方法(初始化数据)、executeCB、completeCB之间传递数据 struct AddonData { @@ -227,7 +267,9 @@ static napi_value addPromise(napi_env env, napi_callback_info info) { ... } ``` + ### 创建异步工作项 + 同Callback方式在创建异步工作项前,我们先分别声明2个函数,分别用作于napi_create_async_work()函数的execute、complete参数。异步工作项创建OK后,将其存入上下文数据的asyncWork属性,并调用napi_queue_async_work()将异步工作项加入调度队列,由异步work线程池统一调度,原生方法返回Promise对象退出。 ```c++ @@ -254,8 +296,11 @@ static napi_value addPromise(napi_env env, napi_callback_info info) { return promise; } ``` -### execute 回调处理 + +### execute 回调处理 + 此处完全同Callback方式,无需修改。 + ```c++ // 业务逻辑处理函数,由worker线程池调度执行。 static void addExecuteCB(napi_env env, void *data) { @@ -265,8 +310,11 @@ static void addExecuteCB(napi_env env, void *data) { addonData->result = addonData->args[0] + addonData->args[1]; } ``` + ### complete 回调处理 + 调用NAPI提供的napi_resolve_deferred() 或 napi_reject_deferred() 返回数据。之后释放过程中创建的napi_ref引用对象、异步工作项等对象。 + ```c++ static void addPromiseCompleteCB(napi_env env, napi_status status, void *data) { AddonData *addonData = (AddonData *)data; @@ -285,7 +333,9 @@ static void addPromiseCompleteCB(napi_env env, napi_status status, void *data) { addonData = nullptr; } ``` + ### eTS调用接口 + ```js import testNapi from "libentry.so"; @@ -310,8 +360,11 @@ struct Index { .height('100%') } ``` + ## 规范异步接口 + 如本文开头所说,若引擎开启Promise特性支持,则异步方法必须同时支持Callback方式和Promise方式,通过判断接收到的参数个数判断是Callback方式还是Promise方式。下面我们将addCallbak()、addPromise() 2个接口合并成一个接口——addAsync(),接口的eTS定义: + ```js function addAsync(num1: number, num2: number, callback:(result: number) => void): void; function addAsync(num1: number, num2: number): Promise; @@ -328,7 +381,9 @@ struct AddonData { double result = 0; }; ``` + 修改接口原生方法实现,通过判断实际获取到的参数个数判断是Callback还是Promise,根据上面的接口定义,2个参数是Promise,3个参数是Callback。 + ```c++ static napi_value addAsync(napi_env env, napi_callback_info info) { // 获取3个参数,值的类型是js类型(napi_value) @@ -404,7 +459,9 @@ static napi_value addAsync(napi_env env, napi_callback_info info) { } } ``` + ## 参考资料 + - [napi数据类型转换](./hello_napi.md)。 - [深入浅出 OpenHarmony NAPI 之异步调用](https://gitee.com/javen678/hello-ohos-napi/blob/master/doc/3.Callback&Promise.md)。 - [OpenHarmony 知识体系](https://gitee.com/openharmony-sig/knowledge)。 diff --git "a/docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/napi_data_type.md" b/docs/napi_study/docs/napi_data_type.md similarity index 92% rename from "docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/napi_data_type.md" rename to docs/napi_study/docs/napi_data_type.md index ed8e32eb45765a51c05613340ed442556e81c6c5..4907473ed8a9ebcf00c04fd799ca65e0ec65691f 100755 --- "a/docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/napi_data_type.md" +++ b/docs/napi_study/docs/napi_data_type.md @@ -1,27 +1,40 @@ # NAPI数据类型转换与同步调用 + ## napi的数据类型 + 在[通过IDE创建工程](./hello_napi.md)的示例代码中,我们使用napi_create_string_utf8函数将C/C++ string转换成NAPI类型——napi_value 。OpenHarmony NAPI将ECMAScript标准中定义的Boolean、Null、Undefined、Number、BigInt、String、Symbol和Object八种数据类型,以及函数对应的Function类型,统一封装成napi_value类型 (也称为JS类型)。该类型用于接收应用传递过来的数据以及返回数据给应用。 本文将讲述JS类型和C/C++数据类型之间的转换 。 + ## napi的数据类型转换接口 + OpenHarmony NAPI提供了很多接口用来napi的数据类型与C/C++数据类型之间的转换。以下列出常用的几种接口 + ### C/C++数据类型转napi类型 + - napi_create_double(napi_env env, double value, napi_value* result); - napi_create_int32(napi_env env, int32_t value, napi_value* result); - napi_create_uint32(napi_env env, uint32_t value, napi_value* result); - napi_create_int64(napi_env env, int64_t value, napi_value* result); - napi_create_string_utf8(napi_env env, const char* str, size_t length, napi_value* result); + ### napi类型转C/C++数据类型 + - napi_get_value_double(napi_env env, napi_value value, double* result); - napi_get_value_int32(napi_env env, napi_value value, int32_t* result); - napi_get_value_uint32(napi_env env, napi_value value, uint32_t* result); - napi_get_value_int64(napi_env env, napi_value value, int64_t* result); - napi_get_value_string_utf8(napi_env env, napi_value value, char* buf, size_t bufsize, size_t* result); - napi_get_value_bool(napi_env env, napi_value value, bool* result); + ## 同步调用 + 我们在[通过IDE创建工程](./hello_napi.md)示例中添加一个简单的接口——add(num1, num2)来讲述具体细节,以及接口同步方式的实现。
NAPI的同步方式调用的扩展API代码处理流程如下图。
- ![同步调用扩展API实现方法的代码处理流程](media/NAPI-SyncWorkFlow.png) -### 添加NAPI扩展API + ![同步调用扩展API实现方法的代码处理流程](media/NAPI-SyncWorkFlow.png) + +### 添加NAPI扩展API + - NAPI定义并注册接口 + ```c++ static napi_value add(napi_env env, napi_callback_info info) { napi_value reuslt; @@ -34,13 +47,18 @@ NAPI的同步方式调用的扩展API代码处理流程如下图。
}; } ``` + - eTs定义接口 - ``` + + ```ts function add(num1: number, num2: number): number; ``` + ### 获取JS参数 + NAPI定义API方法时的接收参数为(napi_env, napi_callback_info),其中napi_callback_info为上下文的信息。通过NAPI提供了napi_get_cb_info()方法可从napi_callback_info中获取参数列表、this及其他数据。 - napi_get_cb_info函数说明如下 : + napi_get_cb_info函数说明如下: + ```c++ napi_status napi_get_cb_info(napi_env env, napi_callback_info cbinfo, @@ -49,14 +67,18 @@ napi_status napi_get_cb_info(napi_env env, napi_value* thisArg, void** data) ``` + 参数说明: + - [in] env: 传入接口调用者的环境,包含js引擎等,由框架提供,默认情况下直接传入即可. - [in] value: napi_callback_info对象,上下文的信息 - [in-out] argc: argv数组的长度。若napi_callback_info中实际包含的参数的个数大于请求的数量argc,将只复制argc的值所指定数量的参数只argv中。若实际的参数个数小于请求的数量,将复制全部的参数,数组多余的空间用空值填充,并将参数实际长度写入argc。 - [out] argv: 用于接收参数列表 - [out] thisArg: 用于接收this对象 - [out] data: NAPI的上下文数据 返回值:返回napi_ok表示转换成功,其他值失败。下面的返回napi_status方法一样。 + 在add方法中,调用napi_get_cb_info函数: + ```c++ // env、info 参数由NAPI框架传入 static napi_value add(napi_env env, napi_callback_info info) { @@ -67,8 +89,11 @@ static napi_value add(napi_env env, napi_callback_info info) { return reuslt; } ``` -### JS类型值转换为C/C++类型的值 + +### JS类型值转换为C/C++类型的值 + 因传入的参数是Javascript值类型,并被框架封装成统一的唯一类型——napi_value类型,为了能够进行计算,我们需要获取其对应在C/C++中的类型的值。我们可以用[napi类型转C/C++数据类型](./napi_data_type.md#napi类型转cc数据类型)中的相关接口进行转换。此示例中我们用 napi_get_value_double方式: + ```c++ static napi_value add(napi_env env, napi_callback_info info) { // 获取2个参数,值的类型是js类型(napi_value) @@ -84,8 +109,11 @@ static napi_value add(napi_env env, napi_callback_info info) { return reuslt; } ``` -### 业务逻辑计算 + +### 业务逻辑计算 + 使用Native代码(C/C++代码)完成业务场景逻辑计算。本示例中就简化为2个数字简单相加,代码如下: + ```c++ static napi_value add(napi_env env, napi_callback_info info) { ... @@ -94,29 +122,38 @@ static napi_value add(napi_env env, napi_callback_info info) { ... } ``` + ### 计算结果转换为JS类型并返回 + 计算的结果是C/C++ double类型,不能直接返回给JS,需要转换成NAPI node_value类型 。本示例中我们用到 [C/C++数据类型转napi类型](./napi_data_type.md#cc数据类型转napi类型)中的napi_create_double接口: + ```c++ static napi_value add(napi_env env, napi_callback_info info) { - ... - // 将结果由C++类型(double)转换成js类型(napi_value) - napi_value reuslt; - napi_create_double(env, sum, &reuslt) - // 将结果返回到JS - return result; + ... + // 将结果由C++类型(double)转换成js类型(napi_value) + napi_value reuslt; + napi_create_double(env, sum, &reuslt) + // 将结果返回到JS + return result; } ``` + 完整的示例代码参照[完整源码](../../../FA/NapiStudy_HellNapi/entry/src/main/cpp/hello.cpp)。 + ### 应用调用 + 本示例程序也是在DevEco Studio开发,应用调用可参照[通过IDE创建工程](./hello_napi.md) -``` + +```ts import testNapi from "libentry.so" let num1 = 123 let num2 = 456 let result = testNapi.add(num1, num2) ``` + ## 参考资料 + - [通过IDE开发一个napi工程之Hello napi](./hello_napi.md) - [Hello napi完整源码工程](../../../FA/NapiStudy_HellNapi) - [深入浅出 OpenHarmony NAPI 之数据类型转换](https://gitee.com/javen678/hello-ohos-napi/blob/master/doc/2.%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B%E8%BD%AC%E6%8D%A2.md) diff --git "a/docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/napi_export_object.md" b/docs/napi_study/docs/napi_export_object.md similarity index 41% rename from "docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/napi_export_object.md" rename to docs/napi_study/docs/napi_export_object.md index 549f2a7db662574d71939510de6f69518cf0164f..841a7258575d45f7cef73c781b968cdcfc2e7045 100755 --- "a/docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/napi_export_object.md" +++ b/docs/napi_study/docs/napi_export_object.md @@ -1,67 +1,20 @@ -# NAPI 导出对象 +# NAPI 导出类对象 + ## 简介 -导出对象 是指 Napi通过一种绑定C++类和实例的方法,使JS应用可以调用类的构造函数和方法。 -## NAPI导出对象流程 -- 使用`napi_define_class`API 定义一个JS类
- 其中`napi_define_class`包含与 C++ 类对应的构造函数、静态属性和方法以及实例属性和方法。 -- 通过`napi_wrap`将 C++ 实例绑定在 JS对象中
- 当 JS代码调用构造函数时,构造函数回调会使用`napi_wrap`将一个新的 C++ 实例绑定在 JS对象中,然后返回绑定对象。 -- 获取目标的 C++ 实例
- 当 JS代码调用类上的方法或属性访问器时,会调用相应的`napi_callback`C++ 函数。对于实例回调,`napi_unwrap`获取作为调用目标的 C++ 实例 。 -## NAPI导出对象接口说明 -NAPI接口详细说明可以查看[Node.js](https://nodejs.org/api/n-api.html)官网介绍。 -### `napi_define_class` -```c++ -napi_status napi_define_class(napi_env env, - const char* utf8name, - size_t length, - napi_callback constructor, - void* data, - size_t property_count, - const napi_property_descriptor* properties, - napi_value* result); -``` -这个函数用来定义一个js类,包括: -- 具有类名的 JavaScript 构造函数。绑定相应的 C++ 类时,通过传递的回调`constructor`可用于实例化一个新的 C++ 类实例,然后可以将其放置在使用构造的 JavaScript 对象实例中`napi_wrap`。 -- 构造函数上的属性,其实现可以调用 C++ 类的相应*静态*数据属性、访问器和方法(由具有属性的属性描述符定义`napi_static`)。 -- 构造函数`prototype`对象的属性。绑定 C++ 类时,可以从属性描述符中给出的静态函数调用C++ 类的*非静态*`napi_static`数据属性、访问器和方法,而无需使用 `napi_unwrap`。
-绑定 C++ 类时,传递的 C++ 构造函数回调`constructor` 应该是调用实际类构造函数的类上的静态方法,然后将新的 C++ 实例绑定在 js对象中,并返回绑定对象。详情请参阅`napi_wrap`。
-从中返回的 JavaScript 构造函数`napi_define_class`通常被保存并稍后用于从本机代码构造类的新实例,和/或检查提供的值是否是类的实例。在这种情况下,为了防止函数值被垃圾回收,可以使用创建对它的强持久引用 `napi_create_reference`,确保引用计数保持 >= 1。 -### `napi_wrap` -```c -napi_status napi_wrap(napi_env env, - napi_value js_object, - void* native_object, - napi_finalize finalize_cb, - void* finalize_hint, - napi_ref* result); -``` -这个函数用来将C++实例对象绑定在JS对象中。
-当 JS代码为使用 定义的类调用构造函数时,将 `napi_define_class()`调用`napi_callback`构造函数的 。在构造原生类的实例后,回调必须调用 `napi_wrap()`以将新构造的实例绑定在已创建的 JS 对象中,该对象是`this`构造函数回调的参数。(该`this`对象是从构造函数创建的`prototype`,因此它已经定义了所有实例属性和方法)。
-*特别说明*: -- 绑定C++实例的对象需要注意其生命周期,如果使用的对象其生命周期远大于C++实例,在绑定前需要先通过 - napi_get_reference_value() 获取生命周期。在对象回收时使用napi_delete_reference()清除生命周期。 -- 一个对象智能绑定一个实例。要将另一个C++实例与已绑定过实例的对象再次进行绑定,需要先使用`napi_remove_wrap()`进行解绑。 -### `napi_unwrap` -```c++ -napi_status napi_unwrap(napi_env env, - napi_value js_object, - void** result); -``` -获取绑定在js对象中的C++实例。
-当 JS代码调用类上的方法或属性访问器时,相应`napi_callback`的会被调用。如果回调是针对实例方法或访问器的,则`this`回调的参数是绑定对象;然后可以通过调用`napi_unwrap()`来获得作为调用目标的绑定 C++ 实例。 -### `napi_remove_wrap` -```c++ -napi_status napi_remove_wrap(napi_env env, - napi_value js_object, - void** result); -``` -检索JS对象使用`napi_wrap()`是否绑定过C++实例并删除绑定的C++实例。 -## NAPI导出对象具体实现 -这里我们以NapiTest的为例 +js调用napi的数据,对于简单的数据类型,只需要napi返回对应类型的napi_value数据即可 (详情参照napi数据类型类型与同步调用)。但是对于一些复杂的数据类型(如我们常用C++的类对象),是不能直接返回一个napi_value数据的。这时我们需要对这些数据进行一系列操作后将其导出,这样js才能使用导出后的对象。 +本文以导出类对象为例来说明napi导出对象的具体过程。
+类对象导出的具体过程:
+![类对象导出流程](./media/export_class.png) + +## NAPI导出类对象具体实现 + +这里我们以导出NapiTest类为例说明导出一个类的实现过程 + ### 定义NapiTest类以及相关方法 -新建一个NapiTest类(NapiTest.h),实现类接口的声明: + +NapiTest类主要实现了接收js设置的数据并将该数据返回到js应用中,具体定义如下(NapiTest.h): + ```c++ class NapiTest { public: @@ -69,69 +22,88 @@ public: } ~NapiTest(); - // 创建NapiTest类的实体,并将实体返回到应用端 - static napi_value Create(napi_env env, napi_callback_info info); - - // 初始化js类并设置对应属性并将其导出。 - static napi_value Init(napi_env env, napi_value exports); + static napi_value Create(napi_env env, napi_callback_info info); // 创建NapiTest类的实体,并将实体返回到应用端,该方法为js创建一个类实体,因此需要将该接口对外导出 + static napi_value Init(napi_env env, napi_value exports); // 初始化js类并设置对应属性并将其导出。 private: - // 对外导出的方法 - static napi_value SetMsg(napi_env env, napi_callback_info info); - // 对外导出的方法 - static napi_value GetMsg(napi_env env, napi_callback_info info); - // 定义js结构体时实际的构建函数 - static napi_value Constructor(napi_env env, napi_callback_info info); - // 释放资源的函数(类似类的析构函数) - static void Destructor(napi_env env, void *nativeObject, void *finalize); + static napi_value SetMsg(napi_env env, napi_callback_info info); // 设置数据,此方法给到js直接调用,因此需要将该接口对外导出 + static napi_value GetMsg(napi_env env, napi_callback_info info); // 获取数据,此方法给到js直接调用,因此需要将该接口对外导出 + static napi_value Constructor(napi_env env, napi_callback_info info); // 定义js结构体时实际的构建函数 + static void Destructor(napi_env env, void *nativeObject, void *finalize); // 释放资源的函数(类似类的析构函数) - // 生命周期变量 - static napi_ref sConstructor_; - // 对外导出方法使用到的变量 - static std::string _msg; - // 记录环境变量 - napi_env mEnv = nullptr; - // 记录生命周期变量 - napi_ref mRef = nullptr; + static napi_ref sConstructor_; // 生命周期变量 + static std::string _msg; // 设置和获取数据的变量 + napi_env mEnv = nullptr; // 记录环境变量 + napi_ref mRef = nullptr; // 记录生命周期变量 }; ``` + ### 将NapiTest定义为js类 + - 在定义js类之前,需要先设置类对外导出的方法 + ```c++ napi_property_descriptor desc[] = { - { "getMsg", nullptr, NapiTest::GetMsg, nullptr, nullptr, nullptr, + { "getMsg", nullptr, NapiTest::GetMsg, nullptr, nullptr, nullptr, napi_default, nullptr }, { "setMsg", nullptr, NapiTest::SetMsg, nullptr, nullptr, nullptr, - napi_default, nullptr }, + napi_default, nullptr }, } ``` + - 定义js类 + ```c++ napi_value mConstructor = nullptr; if (napi_define_class(env, NAPI_CLASS_NAME, NAPI_AUTO_LENGTH, Constructor, nullptr, - sizeof(desc) / sizeof(desc[0]), desc, &mConstructor) != napi_ok) { + sizeof(desc) / sizeof(desc[0]), desc, &mConstructor) != napi_ok) { return nullptr; } ``` - 其中:
- ​ NAPI_CLASS_NAME是对外导出的类名
- ​ Constructor是处理类的构造实例的回调函数
- ​ desc 属性描述符数组,描述类的静态和实例数据属性和方法
- ​ mConstructor 代表类的构造函数
- 当我们从js端new这个类时,我们需要实现构造实例的回调函数Constructor: + + 使用到函数说明: + + ```c++ + napi_status napi_define_class(napi_env env, + const char* utf8name, + size_t length, + napi_callback constructor, + void* data, + size_t property_count, + const napi_property_descriptor* properties, + napi_value* result); + ``` + + 功能:将C++类定义为js的类
+ 参数说明: + - [in] env: 调用api的环境 + - [in] utf8name: C++类的名字 + - [in] length: C++类名字的长度,默认自动长度使用NAPI_AUTO_LENGTH + - [in] constructor: 处理构造类实例的回调函数 + - [in] data: 作为回调信息的数据属性传递给构造函数回调的可选数据 + - [in] property_count: 属性数组参数中的个数 + - [in] properties: 属性数组 + - [out] result: 通过类构造函数绑定类实例的napi_value对象 + + 返回:调用成功返回0,失败返回其他 + +- 实现js类的构造函数 + + 当js应用通过new方法获取类对象的时候,此时会调用 napi_define_class 中设置 constructor 回调函数,该函数实现方法如下: + ```c++ napi_value NapiTest::Constructor(napi_env env, napi_callback_info info) { - napi_value undefineVar = nullptr, thisVar = nullptr; + napi_value undefineVar = nullptr, thisVar = nullptr; napi_get_undefined(env, &undefineVar); if (napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr) == - napi_ok && thisVar != nullptr) { + napi_ok && thisVar != nullptr) { // 创建NapiTest 实例 NapiTest *reference = new NapiTest(env); // 绑定实例类创建NapiTest到导出的对象result if (napi_wrap(env, thisVar, reinterpret_cast(reference), - NapiTest::Destructor, nullptr, &(reference->mRef)) == napi_ok) { + NapiTest::Destructor, nullptr, &(reference->mRef)) == napi_ok) { return thisVar; } @@ -141,7 +113,9 @@ private: return undefineVar; } ``` - 其中NapiTest::Destructo方法是用来释放创建的对象 + + 其中NapiTest::Destructo方法是用来释放创建的对象: + ```c++ void NapiTest::Destructor(napi_env env, void *nativeObject, void *finalize) { @@ -149,31 +123,61 @@ private: test->~NapiTest(); } ``` + + 使用到函数说明: + + ```c++ + napi_status napi_wrap(napi_env env, + napi_value js_object, + void* native_object, + napi_finalize finalize_cb, + void* finalize_hint, + napi_ref* result); + ``` + + 功能:将C++类实例绑定到js对象,并关联对应的生命周期
+ 参数说明: + - [in] env: 调用api的环境 + - [in] js_object: 绑定C++类实例的js对象 + - [in] native_object: 类实例对象 + - [in] finalize_cb: 释放实例对象的回调函数 + - [in] finalize_hint: 传递给回调函数的数据 + - [out] result: 绑定js对象的引用 + + 返回:调用成功返回0,失败返回其他 + ### 导出js类 + - 创建生命周期(生命周期相关可以参考文档[napi生命周期](./napi_life_cycle.md))
在设置类导出前,需要先创建生命周期 + ```c++ if (napi_create_reference(env, mConstructor , 1, &sConstructor_) != napi_ok) { - return nullptr; + return nullptr; } ``` + mConstructor 定义js类时返回的代表类的构造函数的数据
sConstructor_ 生命周期变量
- 将类导出到exports中 将类以属性值的方式导出 + ```c++ - if (napi_set_named_property(env, exports, NAPI_CLASS_NAME, constructor) != napi_ok) { - return nullptr; + if (napi_set_named_property(env, exports, NAPI_CLASS_NAME, constructor) != napi_ok) { + return nullptr; } ``` + +通过以上步骤,我们基本实现了NapiTest这个类的导出。
注意:以上实现都是在类的Init方法中,我们只需要在NAPI注册的接口中调用该Init即可。完整代码可以查看[NapiTest源码](../../../FA/NapiStudy_ObjectWrapTest/entry/src/main/cpp/NapiTest.cpp) + ### 创建类的实例对象 -为了确保应用能正常获取到类的导出,我们需要确保应用层能够正确获取到类的对象实例。
-应用层获取实例对象的方式有两种,一是应用层直接通过new 该类对象获取,另一种调用napi接口,通过napi实现new一个类的对象。本文将以第二种方式来实现。
-我们在NapiTest类中定义了Create方法,该方法就是返回一个类的实例对象: + +js应用除了调用new方法获取类的实例外,我们也可以提供一些方法让js应用获取对应的类的实例,如在我们的NapiTest类中,我们定义了一个Create方法,该方法实现了NapiTest类实例的获取。具体实现如下: + ```c++ napi_value NapiTest::Create(napi_env env, napi_callback_info info) { - napi_status status; + napi_status status; napi_value constructor = nullptr, result = nullptr; // 获取生命周期变量 status = napi_get_reference_value(env, sConstructor_, &constructor); @@ -190,10 +194,16 @@ napi_value NapiTest::Create(napi_env env, napi_callback_info info) { return nullptr; } ``` -在NAPI接口的注册中将该方法以接口的方式导出,应用层就可以直接调用该接口并获取到该类的实例对象。 + +在napi接口的注册中将该方法以接口的方式导出,应用层就可以直接调用该接口并获取到该类的实例对。
+特别说明:如果单独实现了一个类实例获取的方法,那么js的类构造函数可以不实现。 + ### 实现NAPI接口的注册 + 我们已helloworld为列, + - 新建一个hello.cpp,定义模块 + ```c++ static napi_module demoModule = { .nm_version =1, @@ -205,17 +215,26 @@ napi_value NapiTest::Create(napi_env env, napi_callback_info info) { .reserved = { 0 }, }; ``` + - 实现模块的Init + ```c++ EXTERN_C_START static napi_value Init(napi_env env, napi_value exports) { - // do some init - return exports; + napi_property_descriptor desc[] = { + { "create", nullptr, NapiTest::Create, nullptr, nullptr, nullptr, napi_default, nullptr } // 单独导出 create 方法,js应用可以直接调用Create方法获取类实例 + }; + + napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); + + return NapiTest::Init(env, exports); // 导出类以及类的方法 } EXTERN_C_END ``` + - 模块注册 + ```c++ // 注册 hello模块 extern "C" __attribute__((constructor)) void RegisterHelloModule(void) @@ -223,24 +242,15 @@ napi_value NapiTest::Create(napi_env env, napi_callback_info info) { napi_module_register(&demoModule); } ``` -- 在模块中调用NapiTest类的导出
- 在模块Init中添加上NapiTest的Init,并将类的实例创建方法导出 - ```c++ - EXTERN_C_START - static napi_value Init(napi_env env, napi_value exports) - { - napi_property_descriptor desc[] = { - { "create", nullptr, NapiTest::Create, nullptr, nullptr, nullptr, - napi_default, nullptr } - }; - napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); - return NapiTest::Init(env, exports); - } - EXTERN_C_END - ``` + +至此,我们完成了整个napi接口注册以及napi类的导出。 + ## 应用调用NAPI实例 + ### 导出接口 + 在使用该NAPI的时候,我们需要在ts文件(路径在\entry\src\main\cpp\types\libentry\index.d.ts),声明以下内容: + ```js export const create : () => NapiTest; export class NapiTest { @@ -248,14 +258,21 @@ export class NapiTest { getMsg(): string; } ``` + 该文件申明了NAPI接口中导出的方法和类 + ### 应用调用 -新建一个helloworld的ETS工程,该工程中包含一个按键 + +新建一个helloworld的ETS工程,该工程中包含一个按键,我们可以通过该按键进行数据的在native C++中存储和获取 + - 导出napi对应的库(之前NAPI接口生成的库名为libentry.so) + ```js import testNapi from "libentry.so"; ``` + - 定义变量 tt + ```js struct Index { @State message: string = 'Hello World' @@ -276,23 +293,29 @@ export class NapiTest { .height('100%') } ``` + - 在按键中调用对应的接口并输出内容 + ```js if (this.falg == 0) { - this.flag = 2 - this.tt.setMsg("1+1") + this.flag = 2 + this.tt.setMsg("1+1") } else { - this.flag = 0 - this.tt.setMsg("1-1") + this.flag = 0 + this.tt.setMsg("1-1") } console.info("[NapiTest]:" + this.tt.getMsg() + " = " + this.flag); ``` + 通过IDE LOG信息可以查看到,当按多次下按钮时,出现交替以下信息: + ```js 02200/JsApp: [NapiTest]: 1+1 = 2 02200/JsApp: [NapiTest]: 1-1 = 0 ``` + ## 参考资料 + - [NapiTest源码工程](../../../FA/NapiStudy_ObjectWrapTest) - [通过IDE开发一个napi工程](./hello_napi.md) - [Node.js](https://nodejs.org/api/n-api.html) diff --git "a/docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/napi_life_cycle.md" b/docs/napi_study/docs/napi_life_cycle.md similarity index 54% rename from "docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/napi_life_cycle.md" rename to docs/napi_study/docs/napi_life_cycle.md index d7261901e18c29703ee1269cf3b7b2ca8e37640d..105c024e311b17818ea236b5a1e3a3efe383da27 100755 --- "a/docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/napi_life_cycle.md" +++ b/docs/napi_study/docs/napi_life_cycle.md @@ -1,19 +1,23 @@ -## 什么是NAPI的生命周期管理 - -我们都知道,程序的生命周期是指程序从启动,运行到最后的结束的整个过程。生命周期的管理自然是指控制程序的启动,调用以及结束的方法。
-而NAPI中的生命周期的管理又是怎样的呢?以下是Node.js官网给出的说明:
-[ECMAScript 语言规范](https://tc39.github.io/ecma262/)将“代理”的概念定义为运行 JavaScript 代码的自包含环境。多个这样的代理可以由进程同时或按顺序启动和终止。
-一个 Node.js 环境对应一个 ECMAScript 代理。在主进程中,在启动时会创建一个环境,并且可以在单独的线程上创建额外的环境作为[工作线程](https://nodejs.org/api/worker_threads.html)。当 Node.js 嵌入到另一个应用程序中时,该应用程序的主线程也可能在应用程序进程的生命周期中多次构建和销毁一个 Node.js 环境,这样应用程序创建的每个 Node.js 环境都可以在反过来,在其生命周期中创建和销毁其他环境作为工作线程。
-从本机插件的角度来看,这意味着它提供的绑定可以从多个上下文甚至从多个线程同时调用多次。
-原生插件可能需要分配它们在整个生命周期中使用的全局状态,这样状态对于插件的每个实例必须是唯一的。
-为此,Node-API 提供了一种分配数据的方法,使其生命周期与代理的生命周期相关联。 +# NAPI生命周期 + +## 什么是NAPI的生命周期 + +我们都知道,程序的生命周期是指程序从启动,运行到最后的结束的整个过程。生命周期的管理自然是指控制程序的启动,调用以及结束的方法。 +而NAPI中的生命周期又是怎样的呢?如下图所示:
+![napi life](./media/life_cycle.png)
+从图上我们可以看出,在js应用启动时会加载napi模块,而在napi模块加载过程中会创建一个napi对象A提供给应用使用,在应用退出或者主动释放A对象前,A对象必须一直保持"活跃"状态。从A对象创建到释放的整个过程也代表着A对象的生命周期。 + ## NAPI生命周期管理的方法 + js调用时,NAPI中对象的句柄可以作为napi_value返回. 这些句柄必须保持对象“活动”,直到本机代码不再需要它们,否则可以在本机代码完成使用它们之前回收对象。
当返回对象句柄时,它们与“范围”相关联。默认范围的生命周期与本机方法调用的生命周期相关联。结果是,默认情况下,句柄保持有效,并且与这些句柄关联的对象将在本机方法调用的生命周期内保持活动状态。
但是,在许多情况下,与本地方法相比,句柄必须在更短或更长的生命周期内保持有效。此时,NAPI提供了对应的函数来改变默认句柄的寿命(即生命周期)。 + ### 设置局部生命周期 + 因为在napi中全部js相关的值都是一个不透明的封装,默认生命周期是和全局一致的,有时候处于安全和性能的考虑,须要将一些值的生命周期限制在必定的范围之内,此时我们就需要用到NAPI相关的接口来napi_open_handle_scope和napi_close_handle_scope建立和关闭一个上下文环境。比如: -``` + +```c++ for (int i = 0; i < 1000000; i++) { napi_handle_scope scope; napi_status status = napi_open_handle_scope(env, &scope); @@ -32,41 +36,75 @@ for (int i = 0; i < 1000000; i++) { } } ``` -此时,因为限制了做用域,因此每一个result的生命周期都被限制在了单次循环以内。 + +此时,因为限制了做用域,因此每一个result的生命周期都被限制在了单次循环以内。
+使用到的函数: + +```c++ +napi_status napi_open_handle_scope(napi_env env, napi_handle_scope* result) +``` + +功能:打开一个局部的生命周期
+参数说明: + +- [in] env - 当前环境变量 +- [out] result - 根据当前环境创建的生命周期变量 + +返回:napi_status,成功返回0,失败返回其他 + +```c++ +napi_status napi_close_escapable_handle_scope(napi_env env, napi_handle_scope scope) +``` + +功能:关闭传入的生命周期(生命周期必须按照创建它们的相反顺序关闭)。
+参数说明: + +- [in] env - 当前环境变量 +- [out] scope - 需要关闭的生命周期变量 + +返回:napi_status,成功返回0,失败返回其他 + ### 设置全局生命周期 - 在某些情况下,插件需要能够创建和引用具有比单个本地方法调用更长的生命周期的对象。例如,要创建一个构造函数并稍后在请求中使用该构造函数来创建实例,必须可以在许多不同的实例创建请求中引用该构造函数对象。`napi_value`如前面部分所述,如果返回正常的句柄,这是不可能的。普通句柄的生命周期由作用域管理,所有作用域必须在本机方法结束之前关闭。
- NAPI 提供了创建对对象的持久引用的方法。每个持久引用都有一个关联的计数,其值为 0 或更高。计数确定引用是否会使相应的对象保持活动状态。计数为 0 的引用不会阻止对象被收集,通常称为“弱”引用。任何大于 0 的计数都将阻止对象被收集。
- 可以使用初始引用计数创建引用。然后可以通过`napi_reference_ref`和`napi_reference_unref`修改计数。如果在引用计数为 0 时收集了一个对象,则所有后续获取与该引用关联的对象的调用`napi_get_reference_value`都将返回`NULL`的`napi_value`. 尝试调用 `napi_reference_ref`其对象已被收集的引用会导致错误。
- 一旦插件不再需要引用,就必须删除它们。当一个引用被删除时,它不会再阻止相应的对象被收集。未能删除持久引用会导致“内存泄漏”,持久引用的本机内存和堆上的相应对象都将永久保留。
- 可以创建多个引用同一个对象的持久引用,每个引用都将根据其单独的计数保持对象处于活动状态或不处于活动状态。对同一对象的多个持久引用可能会导致本机内存意外保持活动状态。持久引用的本机结构必须保持活动状态,直到被引用对象的终结器被执行。如果为同一个对象创建了新的持久引用,则该对象的终结器将不会运行,并且早期持久引用指向的本机内存将不会被释放。这可以通过需要的情况下调用`napi_delete_reference`在`napi_reference_unref`来避免。
-## NAPI管理生命周期的相关函数 -生命周期相关函数的具体信息可以参照[Node.js官网](https://nodejs.org/api/n-api.html#object-lifetime-management)。 -### 局部生命周期API -- napi_status napi_open_handle_scope(napi_env env, napi_handle_scope* result)
- 打开一个新的生命周期。 -- napi_status api_close_handle_scope(napi_env env, napi_handle_scope scope)
- 关闭传入的生命周期,生命周期必须按照创建它们的相反顺序关闭。 -- napi_status napi_open_escapable_handle_scope(napi_env env, napi_handle_scope* result)
- 打开一个新生命周期,可以将一个对象提升到外部生命周期。 -- napi_status napi_close_escapable_handle_scope(napi_env env, napi_handle_scope scope)
- 关闭传入的生命周期。生命周期必须按照创建它们的相反顺序关闭.
-- napi_status napi_escape_handle(napi_env env, napi_escapable_handle_scope scope, - napi_value escapee, napi_value* result)
- 提升 JavaScript 对象的句柄,使其在外部作用域的生命周期内有效。每个生命周期只能调用一次。如果多次调用它,将返回错误。 -### 全局生命周期API -- napi_status napi_create_reference(napi_env env, napi_value value, uint32_t initial_refcount, napi_ref *result)
- 用指定的引用计数创建一个新引用到`Object`传入的引用. -- napi_status napi_delete_reference(napi_env env, napi_ref ref)
- 删除传入的引用。 -- napi_status napi_reference_ref(napi_env env, napi_ref ref, uint32_t* result)
- 增加传入引用的引用计数并返回结果引用计数。 -- napi_status napi_reference_unref(napi_env env, napi_ref ref, uint32_t* result)
- 减少传入引用的引用计数并返回结果引用计数。 -- napi_status napi_get_reference_value(napi_env env, napi_ref ref, uint32_t* result)
- 如果仍然有效,将返回napi_value代表Object与napi_ref. 否则,结果将是NULL。 + + 在某些情况下,插件需要能够创建和引用具有比单个本地方法调用更长的生命周期的对象。例如,要创建一个构造函数并稍后在请求中使用该构造函数来创建实例,必须可以在许多不同的实例创建请求中引用该构造函数对象。如有一个napi_value变量constructor,需要将其导出到js使用: + + ```c++ + { + napi_value constructor = nullptr; + ... + if (napi_create_reference(env, constructor, 1, &sConstructor_) != napi_ok) { // 创建生命周期,初始引用计数设为1 + return nullptr; + } + if (napi_set_named_property(env, exports, NAPI_CLASS_NAME, constructor) != napi_ok) { // 设置constructor对象相关属性并绑定到导出变量exports + return nullptr; + } + ... +} + ``` + +此时在其他线程或接口中就可以通过生命周期变量获取此constructor对象进行处理。
+使用到的函数: + +```c++ +napi_status napi_create_reference(napi_env env, napi_value value, uint32_t initial_refcount, napi_ref *result) +``` + +功能:通过引用对象创建新的生命周期引用对象
+参数: + +- [in] env: 当前环境变量 +- [in] value: 需要引用的对象 +- [in] initial_refcount: 引用计数初始值 +- [out] result: 新建的生命周期引用对象 + +返回:napi_status,成功返回0,失败其他 + ## NAPI生命周期管理实现 + 这里我们以TestNapi为例(关于工程创建可以参照[通过IDE开发一个Napi工程](./hello_napi.md)) + - 首先新建一个 hello.cpp,实现 NAPI接口模块的注册 + ```c++ #include "napi/native_api.h" #include @@ -95,7 +133,9 @@ for (int i = 0; i < 1000000; i++) { napi_module_register(&demoModule); } ``` + - 定义一个测试的类(TestNapi) + ```c++ class NapiTest{ public: @@ -104,7 +144,7 @@ for (int i = 0; i < 1000000; i++) { static napi_value SetMsg(napi_env env, napi_callback_info info) { napi_value result = nullptr; napi_get_undefined(env, &result); - char _msg[128] = {0}; + char _msg[128] = {0}; napi_value msgvalue; size_t argc = 1, size = 0; @@ -135,31 +175,39 @@ for (int i = 0; i < 1000000; i++) { } }; ``` + - 定义一个全局的生命周期管理的变量 + ```c++ static napi_ref g_Constructor = nullptr; ``` + - 将类的方法定义到一个napi_property_descriptor的数组
特别申明:此步骤及以下步骤都在initi方法中完成。 + ```c++ static napi_value Init(napi_env env, napi_value exports) { - napi_property_descriptor desc[] = { - { "SetMsg", nullptr, NapiTest::SetMsg, nullptr, nullptr, nullptr, + napi_property_descriptor desc[] = { + { "SetMsg", nullptr, NapiTest::SetMsg, nullptr, nullptr, nullptr, napi_default, nullptr }, - { "GetMsg", nullptr, NapiTest::GetMsg, nullptr, nullptr, nullptr, - napi_default,nullptr }, + { "GetMsg", nullptr, NapiTest::GetMsg, nullptr, nullptr, nullptr, + napi_default,nullptr }, }; } ``` + - 将测试类定义到js类,并创建调用测试类的构造函数 + ```c++ napi_value constructor = nullptr; - if (napi_define_class(env, NAPI_CLASS_NAME, NAPI_AUTO_LENGTH, Constructor, nullptr, sizeof(desc) / sizeof(desc[0]),desc, &constructor) != napi_ok) { - return nullptr; + if (napi_define_class(env, NAPI_CLASS_NAME, NAPI_AUTO_LENGTH, Constructor, nullptr, sizeof(desc) / sizeof(desc[0]),desc, &constructor) != napi_ok) { + return nullptr; } ``` + 其中Constructor构造函数如下: + ```c++ static napi_value Constructor(napi_env env, napi_callback_info info) { napi_value thisVar = nullptr; @@ -169,13 +217,15 @@ for (int i = 0; i < 1000000; i++) { return thisVar; } ``` + 如需要快速释放构建函数中创建的对象,也可以在构造函数中绑定一个类析构函数: + ```c++ static napi_value Constructor(napi_env env, napi_callback_info info) { napi_value thisVar = nullptr; napi_get_undefined(env, &thisVar); napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr); - + std::unique_ptr reference = std::make_unique(); status = napi_wrap(env, thisVar, reinterpret_cast(reference), Destructor, nullptr, nullptr); @@ -185,29 +235,37 @@ for (int i = 0; i < 1000000; i++) { // 类析构函数,释放有Constructor构建时新建的对象 static void Destructor(napi_env env, void *nativeObject, void *finalize) { - NapiTest *test = reinterpret_cast(nativeObject); + NapiTest *test = reinterpret_cast(nativeObject); test->~NapiTest(); } ``` + - 创建生命周期 + ```c++ if (napi_create_reference(env, constructor, 1, &g_Constructor) != napi_ok) { return nullptr; } ``` + - 将生命周期变量作为导出对象的传入属性。 + ```c++ if (napi_set_named_property(env, exports, NAPI_CLASS_NAME, constructor) != napi_ok) { return nullptr; } ``` + - 设置导出对象的属性。 + ```c++ - if (napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc) != napi_ok) { - return nullptr; + if (napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc) != napi_ok) { + return nullptr; } ``` + 到此,我们就完成了js类的定义以及相关生命周期管理的设置,该如何创建生命周期范围内的变量呢?我们可以在NapiTest类中定义一个方法,用于创建在该生命周期范围内的变量: + ```c++ napi_value Create(napi_env env, void *data){ napi_status status; @@ -222,16 +280,34 @@ napi_value Create(napi_env env, void *data){ */ // 创建生命周期内的对象并将其返回 status = napi_new_instance(env, constructor, 0, nullptr, &result); - if (status != napi_ok) { + if (status != napi_ok) { return nullptr; } return result; } ``` + +使用到的函数: + +```c++ +napi_status napi_get_reference_value(napi_env env, napi_ref ref, uint32_t* result) +``` + +功能:获取当前引用的napi_value数据
+参数说明: + +- [in] env: 当前环境变量 +- [in] ref: 引用计数的对象 +- [out] result: 引用计数的对象绑定的napi_value数据 + +返回:napi_status,成功返回0,失败其他 + [完整示例的代码](../../../FA/NapiStudy_ObjectWrapTest/entry/src/main/cpp/NapiTest.cpp) + ## 参考资料 + - [Node.js官网](https://nodejs.org/api/n-api.html#object-lifetime-management) - [如何通过IDE创建napi工程](./hello_napi.md) - [TestNap完整工程](../../../FA/NapiStudy_ObjectWrapTest) -- [OpenHarmony 知识体系](https://gitee.com/openharmony-sig/knowledge) \ No newline at end of file +- [OpenHarmony 知识体系](https://gitee.com/openharmony-sig/knowledge) diff --git "a/docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/add_lib.png" "b/docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/add_lib.png" deleted file mode 100755 index c6c7885d185d032dabc1adeaf74c55cda8892b54..0000000000000000000000000000000000000000 Binary files "a/docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/add_lib.png" and /dev/null differ diff --git "a/docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/add_repath.png" "b/docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/add_repath.png" deleted file mode 100755 index 6cbe38fb889154df9c0341c9a4e13364e4b2a5da..0000000000000000000000000000000000000000 Binary files "a/docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/add_repath.png" and /dev/null differ diff --git "a/docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/download_lib.png" "b/docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/download_lib.png" deleted file mode 100755 index 7f3964d122d21498014c3d222a36c41779026a2a..0000000000000000000000000000000000000000 Binary files "a/docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/download_lib.png" and /dev/null differ diff --git "a/docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/lib_location.png" "b/docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/lib_location.png" deleted file mode 100755 index cf396e0c23a03030a0f021f31cb0de11b7890454..0000000000000000000000000000000000000000 Binary files "a/docs/napi\347\263\273\345\210\227\345\255\246\344\271\240/docs/media/lib_location.png" and /dev/null differ diff --git "a/docs/\345\255\246\344\271\240\350\265\204\346\272\220\346\225\264\345\220\210/readme.md" "b/docs/\345\255\246\344\271\240\350\265\204\346\272\220\346\225\264\345\220\210/readme.md" index ad5517cdd66f8a0c48ac3d7273d1cf9d119eac2e..4083dcb9d8c4b8ec764ab92e92c90fac3dd9b4c4 100644 --- "a/docs/\345\255\246\344\271\240\350\265\204\346\272\220\346\225\264\345\220\210/readme.md" +++ "b/docs/\345\255\246\344\271\240\350\265\204\346\272\220\346\225\264\345\220\210/readme.md" @@ -31,7 +31,6 @@ OpenHarmony旨在为开发者提供NUI(Natural User Interface)的交互方 - [多模输入事件分发机制详解](https://mp.weixin.qq.com/s/XXaahJkXyRusUVTNqX3SGA) - #### 安全子系统 安全子系统包括系统安全、数据安全、应用安全等模块,为OpenHarmony提供了保护系统和和用户数据的能力。安全子系统当前开源的功能,包括应用完整性保护、应用权限管理、设备认证、密钥管理服务。 @@ -82,7 +81,7 @@ OpenHarmony驱动子系统采用C面向对象编程模型构建,通过平台 ## 技术分享 - [OpenHarmony IoT设备开发](https://growing.openharmony.cn/mainPlay/learnPathMaps?id=44) -- [OpenHarmony Napi学习](../napi%E7%B3%BB%E5%88%97%E5%AD%A6%E4%B9%A0/ReadMe.md) +- [OpenHarmony Napi学习](../napi_study/ReadMe.md) ## 应用样例解析