OpenHarmony 源碼解析之 JavaScriptAPI NAPI-C 接口
1 簡介
在《OpenHarmony 源碼解析之JavaScript API框架(NAPI)》一文提到,NAPI就是L2設備上的 JS API實現方式。對OpenHarmony提供的NAPI的設計思想、編程原則、Sample源碼等已做說明,本文不再贅述。
NAPI機制在整個OpenHarmony系統中起著承上啟下的重要作用,而框架代碼中C實現的比例也很大,尤其是對接驅動這方面。
本文主要呈現的是NAPI之C接口的極簡方式實現范例,適合初學者了解打通NAPI流程。
1.1 UI架構相關系列
《OpenHarmony 源碼解析之ACE (JavaScript運行環境初始化)》
《OpenHarmony 源碼解析之JavaScript API框架(NAPI)》
《OpenHarmony 源碼解析之JavaScript API框架(NAPI-C接口)》
《OpenHarmony 源碼解析之JavaScript(文件管理API)》
1.2 OpenHarmony架構圖

1.3 NAPI概念回顧
NAPI在系統中位置示意圖如下:
從圖中可以得出,NAPI就是JS與C/C++之間的調用方式。
1.4 NAPI 實現方式
OpenHarmony上NAPI實現方式有兩種,分別是:
- NAPI之C接口
- NAPI之C++接口
兩者的特點與區別見下圖:

2 NAPI之C接口
2.1 實現過程解析
2.1.1 編譯構建
- 增加子系統
源碼/build/subsystem_config.json中增加子系統選項,如下所示:
- "sample": {
- "project": "hmf/sample",
- "path": "foundation/sample",
- "name": "sample",
- "dir": "foundation"
- }
- 修改產品定義
源碼/productdefine/common/products/Hi3516DV300.json中增加part選項
- "sample:sample_show":{}
- 增加樣例ohos.build文件
源碼路徑新增/foundation/sample/sample_show/ohos.build文件,定義子系統為sample,組件為sample_show,文件內容如下:
- {
- "subsystem": "sample",
- "parts": {
- "sample_show": {
- "module_list": [
- "//foundation/sample/sample_show/interfaces/kits/js/declaration:sample_show", "//foundation/sample/sample_show/interfaces/kits/napi:napi_packages",
- ]
- }
- }
- }
- 增加樣例BUILD.gn文件
源碼路徑新增/foundation/sample/sample_show/interfaces/kits/napi/sample/sample_show/BUILD.gn文件,定義動態庫名稱為sample,對應的源文件為sample.cpp, 則生成的動態庫名為libsample.z.so,燒錄后可以到/system/lib/下找到對應的動態庫。
- ohos_shared_library("sample") {
- include_dirs = []
- sources = [
- "sample.cpp",
- ]
2.1.2 源碼編寫
- 增加NAPI模塊sampleModule的注冊
通過Register()注冊后,sampleModule對應的NAPI接口才能被編譯到系統中,應用才能對接口進行調用。
- extern "C" __attribute__((constructor)) void Register()
- {
- napi_module_register(&sampleModule);
- }
- 新增模塊的定義
sampleModule全局變量定義了當前NAPI模塊對應的模塊名sample,以及當前模塊對外接口注冊函數SampleExport。
- static napi_module sampleModule = {
- .nm_version = 1,
- .nm_flags = 0,
- .nm_filename = nullptr,
- .nm_register_func = SampleExport,
- .nm_modname = "sample",
- .nm_priv = ((void*)0),
- .reserved = { 0 },
- };
- 新增模塊的對外接口
在模塊對外接口SampleExport中,增加NAPI接口定義,對于C語言的NAPI只需增加屬性即可napi_define_properties,不需要定義類。
- static napi_value SampleExport(napi_env env, napi_value exports)
- {
- static napi_property_descriptor desc[] = {
- DECLARE_NAPI_FUNCTION("test", JSTest),
- DECLARE_NAPI_FUNCTION("onCallback", JSCallback),
- };
- napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
- return exports;
- }
- 新增NAPI接口實現函數
樣例中增加了兩個接口,分別是:test、onCallback;
**test:**接口使用場景為JS調用C相關函數;
**onCallback:**接口使用場景為JS定義了一個回調函數JSFuncA,某個時機觸發后,通過C語言調用JSFuncA進行回調處理。
- DECLARE_NAPI_FUNCTION("test", JSTest),
- DECLARE_NAPI_FUNCTION("onCallback", JSCallback),
2.2 接口解析
2.2.1 JS->C 接口解析
JS調用的C接口定義:function test(para1: number): void;
具體NAPI的實現如下:
- static napi_value JSTest(napi_env env, napi_callback_info info)
- {
- size_t argc = 1; //參數個數定義
- napi_value argv[argc];
- napi_value thisVar = nullptr;
- void *data = nullptr;
- NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, &data));
- NAPI_ASSERT(env, argc >= 1, "JSTest Wrong number of arguments"); //參數個數校驗
- napi_value result = nullptr;
- napi_create_int32(env, 0, &result);
- int cPara1; //定義接口入參對應的C語言變量
- for(size_t i= 0; i< argc; i++){
- //參數類型校驗
- napi_valuetype valueType = napi_undefined;
- napi_typeof(env, argv[i], &valueType);
- NAPI_ASSERT(env, valueType == napi_number, "JSTest Wrong argument type. Number expected for parameter 1.");
- if(i== 0) {
- //JS參數轉換為C語言參數,打印其值
- napi_get_value_int32(env, argv[i], &cPara1);
- SAMPLE_LOG("JSTest cPara1 value = %{public}d!", cPara1);
- }else{
- SAMPLE_LOG("JSTest napi_get_value error!");
- return result;
- }
- }
- cFunction(cPara1); //調用框架層對應的c函數,C語言參數傳遞到對應的C語言函數中
- return result;
- }
2.2.2 C->JS 接口解析
C調用JS接口函數定義:function JSCallback(callback: Function): void;
入參即為JS的回調函數,實現如下所示:
- static napi_value JSCallback(napi_env env, napi_callback_info info)
- {
- size_t argc = 1; //參數個數定義
- napi_value argv[argc];
- napi_value thisVar = nullptr;
- void *data = nullptr;
- napi_ref CallbackRef;
- napi_value jsObj, prop1, callback = nullptr;
- NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, &data));
- NAPI_ASSERT(env, argc >= 1, "JSCallback Wrong number of arguments"); //參數個數校驗
- napi_valuetype callbackType = napi_undefined;
- napi_typeof(env, argv[0], &callbackType);
- NAPI_ASSERT(env, callbackType == napi_function, "parameter 1 type mismatch"); //參數類型校驗,傳進來的須是函數類型
- napi_create_reference(env, argv[0], 1, &CallbackRef); //創建引用
- napi_get_reference_value(env, CallbackRef, &callback); //根據引用獲取回調函數callback
- int cPara = 1;
- napi_create_object(env, &jsObj); //創建JS回調函數對應的參數對象
- napi_create_int32(env, cPara, &prop1);
- napi_set_named_property(env, jsObj, "prop", prop1); //設置JS參數對象屬性值
- napi_call_function(env, nullptr, callback, 1, &jsObj, &undefine); //使用生成的JS參數,調用對應的JS回調函數
- napi_value result = nullptr;
- napi_get_undefined(env, &result);
- return result;
- }
3 應用代碼示例
JS應用引用NAPI接口時,須先引用接口定義的對應模塊,才能進行接口的調用。示例如下:
- //此處庫名須與2.1.1.4 節BUILD.gn中定義的庫名一致
- import sample from '@ohos.sample'
- ......
- onTest: function () {
- console.log("sample.test start")
- sample.test(2); //JS調用napi接口
- console.log("sample.test end")
- },
- ......
4 總結
NAPI在OpenHarmony應用開發中必不可少,本文檔拋磚引玉介紹了NAPI之C語言接口的極簡實現方式,方便大家入門,后續隨著學習的深入將不斷完善對NAPI接口實現方式的解讀。
文章相關附件可以點擊下面的原文鏈接前往下載
https://harmonyos.51cto.com/resource/1550