本文來源電子發燒友社區,作者:離北況歸, 帖子地址:https://bbs.elecfans.com/jishu_2308792_1_1.html
本文通過一個Hello OpenHarmony NAPI樣例講述了NPAI接口開發基礎知識。開發基于最新的OpenHarmony3.2Beta3版本及其對應SDK。標準系統開發板為潤和軟件dayu200。(附開發板體驗完整視頻,詳見作者原帖子)
將C/C++ 三方庫移植到OpenHarmony標準系統后,需要通過NAPI框架將其C/C++ 接口轉換成JS/ETS接口給應用層調用。
通過本文您將熟悉:
-
OpenHarmony 標準系統應用開發基于ArkUI框架,開發語言使用JS/eTS。部分業務場景依賴使用現有的C/C++ 庫,或為了獲取更高的性能。OpenHarmony提供NAPI機制,用于規范封裝IO、CPU密集型、OS底層等能力并對外暴露JS接口,通過NAPI實現JS和C/C++代碼的互相訪問.
-
OpenHarmony 中的 N-API 定義了由 JS/ETS 語言編寫的代碼和 native 代碼(使用 C/C++ 編寫)交互的方式,由 Node.js N-API 框架擴展而來。
-
N-API:Native Application Programming Interface(本地應用程序接接口)
-
什么是Node.js N-API 框架
Node.js N-API為開發者提供了一套C/C++ API用于開發Node.js的Native擴展模塊。從Node.js 8.0.0開始,N-API以實驗性特性作為Node.js本身的一部分被引入,并且從Node.js 10.0.0開始正式全面支持N-API。
添加OpenHarmony自定義子系統、組件、模塊
-
這部分內容涉及三方庫移植,為便于本篇NAPI基礎的學習。筆者在此自定義一個子系統用于開發NAPI。如在已存在的子系統組件中添加擴展NAPI,則跳過此步。
-
需要準備好OpenHarmonyBeta3源碼和編譯環境
添加子系統、組件
直接在OpenHarmony源碼根目錄創建子系統文件夾,取名mysubsys。并在目錄下添加子系統的構建配置文件ohos.build
完整內容如下:
{
"subsystem": "mysubsys",
"parts": {
"hello": {
"module_list": [
"http://mysubsys/hello/hellonapi:hellonapi"
],
"inner_kits": [
],
"system_kits": [
],
"test_list": [
]
}
}
}
-
另外ohos.build里面不支持加注釋,后面編譯的時候會莫名其妙報錯。別問,問就是筆者踩過坑了。(好像也沒必要加注釋)
需要明白以下知識點:
"subsystem": "mysubsys",
-
subsystem后面的mysubsy是子系統的名稱。
"parts": {
"hello": {
}
}
-
hello是組件名稱,被mysubsys子系統包含
"module_list": [
"http://mysubsys/hello/hellonapi:hellonapi"
-
hellonapi是模塊名,被hello組件包含。
接著將子系統配置到源碼下buildsubsystem_config.json文件,在該文件中插入如下內容。
"mysubsys": {
"project": "hmf/mysubsys",
"path": "mysubsys",
"name": "mysubsys",
"dir": ""
}
-
OpenHarmony系統架構中,子系統是一個邏輯概念,它具體由對應的組件構成。組件是對子系統的進一步拆分,可復用的軟件單元,它包含源碼、配置文件、資源文件和編譯腳本;能獨立構建,以二進制方式集成,具備獨立驗證能力的二進制單元。
本示例按子系統system > 組件part > 組件module 創建了3級目錄
mysubsys
├── hello
│ └── hellonapi
│ ├── BUILD.gn
│ └── hellonapi.cpp
└── ohos.build
源碼實現
最后在組件目錄下中創建代碼文件hellonapi.cpp
完整內容如下:
#include
#include "napi/native_node_api.h"
#include "napi/native_api.h"
static napi_value getHelloString(napi_env env, napi_callback_info info) {
napi_value result;
std::string words = "Hello OpenHarmony NAPI";
NAPI_CALL(env, napi_create_string_utf8(env, words.c_str(), words.length(), &result));
return result;
}
static napi_value registerFunc(napi_env env, napi_value exports)
{
static napi_property_descriptor desc[] = {
DECLARE_NAPI_FUNCTION("getHelloString", getHelloString),
};
NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc));
return exports;
}
static napi_module hellonapiModule = {
.nm_version = 1,
.nm_flags = 0,
.nm_filename = nullptr,
.nm_register_func = registerFunc,
.nm_modname = "hellonapi",
.nm_priv = ((void*)0),
.reserved = { 0 },
};
extern "C" __attribute__((constructor)) void hellonapiModuleRegister()
{
napi_module_register(&hellonapiModule);
}
代碼解析如下:
接口業務實現C/C++代碼
static napi_value getHelloString(napi_env env, napi_callback_info info) {
napi_value result;
std::string words = "Hello OpenHarmony NAPI";
NAPI_CALL(env, napi_create_string_utf8(env, words.c_str(), words.length(), &result));
return result;
}
添加NAPI接口頭文件
NAPI提供了提供了一系列接口函數,聲明包含如下2個頭文件中,先添加這2個頭文件到hellonapi.cpp
#include "napi/native_api.h"
#include "napi/native_node_api.h"
-
native_api.h和native_node_api.h這兩個頭文件
-
在OpenHarmony-3.1-release源碼下是在//foundation/ace/napi/interfaces/kits目錄下
-
在3.2beta3版本中分別在//foundation/arkui/napi/interfaces/kits和//foundation/arkui/napi/interfaces/inner_api目錄下了。
注冊NAPI模塊、添加接口聲明
定義的hellonapi模塊,其對應結構體為napi_module。
-
指定當前NAPI模塊對應的模塊名以及模塊注冊對外接口的處理函數,具體擴展的接口在該函數中聲明。
static napi_module hellonapiModule = {
.nm_version = 1,
.nm_flags = 0,
.nm_filename = nullptr,
.nm_register_func = registerFunc,
.nm_modname = "hellonapi",
.nm_priv = ((void*)0),
.reserved = { 0 },
};
-
NAPI提供DECLARE_NAPI_FUNCTION(name, func)函數用于聲明api,傳入名稱和其他實現函數。在registerFunc函數中添加DECLARE_NAPI_FUNCTION,本例添加了一個getString接口。
-
模塊定義好后,調用NAPI提供的模塊注冊函數napi_module_register(napi_module* mod)函數注冊到系統中。
static napi_value registerFunc(napi_env env, napi_value exports)
{
static napi_property_descriptor desc[] = {
DECLARE_NAPI_FUNCTION("getHelloString", getHelloString),
};
NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc));
return exports;
}
自定義子系統構建
hellonapi編譯gn化,新增gn工程構建腳本。
在模塊hellonapi目錄下新建BUILD.gn文件,內容如下:
gn文件支持注釋,以#
開頭
import("http://build/ohos.gni")
ohos_shared_library("hellonapi") {
include_dirs = [
"http://foundation/arkui/napi/interfaces/kits",
"http://foundation/arkui/napi/interfaces/inner_api",
"http://third_party/node/src"
]
cflags_cc = [
"-Wno-error",
"-Wno-unused-function",
]
sources = [
"hellonapi.cpp"
]
deps = [ "http://foundation/arkui/napi:ace_napi" ]
relative_install_dir = "module"
subsystem_name = "mysubsys"
part_name = "hello"
}
將組件添加到需要的產品配置文件,源碼目錄下的productdefine/common/products/ohos-arm64.json。
-
插入位置任意,但要注意行尾的逗號,確保格式json文件格式正確。
"parts":{
...
"mysubsys:hello":{},
...
}
-
mysubsys是本示例自定義的子系統名稱
-
hello是自定義子系統下的組件名稱
-
parts格式如下:
"parts":{
"部件所屬子系統名:部件名":{}
}
修改build/subsystem_config.json
新增子系統定義。
-
subsystem_config.json文件定義了有哪些子系統以及這些子系統所在文件夾路徑,添加子系統時需要說明子系統path與name,分別表示子系統路徑和子系統名。
注意json文件也不支持注釋!!!
"mysubsys": {
"project": "hmf/mysubsys",
"path": "mysubsys",
"name": "mysubsys"
}
修改vendor/hihope/rk3568/config.json文件
將mysubsys子系統添加至rk3568開發板,在vendor目錄下新增產品的定義。
{
"subsystem": "mysubsys",
"components": [
{
"component": "hello",
"features": []
}
]
}
編譯燒錄
先進行增量編譯出子系統的動態庫,增量編譯沒有報錯后。再全量編譯出鏡像,將其燒錄到開發板上。
./build.sh --product-name rk3568 --ccache --build-target=hellonapi --target-cpu arm64
-
全量編譯和燒錄
這部分的內容不重復敘述,大家可以參考社區文章。
鏡像文件在源碼目錄下位置如下:
調用接口
full-SDK替換(可選)
從OpenHarmony 3.2 Beta2起,SDK會同時提供Public SDK和Full SDK。通過DevEco Studio默認獲取的SDK為Public SDK。
兩者差異如下:
-
Public SDK
-
面向應用開發者提供,不包含需要使用系統權限的系統接口。通過DevEco Studio默認獲取的SDK為Public SDK。
-
Full SDK
-
面向OEM廠商提供,包含了需要使用系統權限的系統接口。使用Full SDK時需要手動從鏡像站點獲取,并在DevEco Studio中替換
筆者使用的DevEco Studio版本為3.0.0.993,即DevEco Studio 3.0。API為API9。
full-SDK替換請參考官方文檔:full-SDK替換指南
若提示找不到npm,需要配置一下環境變量,將以下路徑添加到環境變量中即可
D:DevEco Studioohossdketsbuild-toolsets-loader
創建OpenHarmony標準應用
新建項目,選擇OpenHarmony。
compile sdk選擇9,其他保持默認即可。
插一句題外話,3.1release版本發布的時候。華為是把DevEco Studio分成了OpenHarmony和HarmonyOS兩個版本的,現在又合并到一起了。
調用接口
-
調用方式和ArkUI框架提供的API一樣,先import引入擴展的NAPI模塊,后直接調用。
index.ets內容如下:
import prompt from '@system.prompt'
import hellonapi from '@ohos.hellonapi'
@Entry
@Component
struct HelloNAPI {
build() {
Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
Button("NAPI: hellonapi.getHelloString()").margin(10).fontSize(24).onClick(() => {
let strFromNAPI = hellonapi.getHelloString()
prompt.showToast({ message: strFromNAPI })
})
}
.width('100%')
.height('100%')
}
}
然后選擇自動簽名
將應用安裝到dayu200開發板上
運行效果如下:
-
也可以參考其他模塊的.d.ts創建擴展模塊@ohos.hellonapi.d.ts定義文件,放到IDE安裝OpenHarmony SDK的目錄路徑ohossdkets3.2.7.5api下。
-
.d.ts文件的命名為@ohos.ohos_shared_library_name.d.ts,ohos_shared_library為BUID.gn文件中定義的動態庫名稱
@ohos.hellonapi.d.ts內容如下:
declare namespace hellonapi {
function getHelloString(): string;
}
export default hellonapi;
-
其中
@syscap SystemCapability.HiviewDFX.HiAppEvent
語句在.d.ts文件中一定要添加,否則IDE還是會報錯找不到該文件。
-
其中
declare namespace hellonapi
的hellonapi是BUILD.gn中的定義的ohos_shared_library_name。
-
其中
function getHelloString(): string;
中的getHelloString()是hellonapi.cpp文件中指定的模塊注冊對外接口的處理函數
IDE問題掃描如下:
如果不新建@ohos.hellonapi.d.ts放在sdkets3.2.7.5api,則IDE會報錯
標準應用編譯不是強依賴OpenHarmony SDK,所以可忽略IDE中告警,直接編譯打包hap。但是有的時候IDE會提示找不到@ohos.hellonapi.d.ts,然后有小概率的機會無法安裝hap。這個時候就要參考ohossdkets3.2.7.5api下的.d.ts文件編寫@ohos.hellonapi.d.ts了。
知識點附送
Native API中支持的標準庫
表1OpenHarmony支持的標準庫
評論