OpenHarmony之NAPI框架介紹

張志成

誠邁科技高級技術專家

NAPI是什么

NAPI的概念源自Nodejs,為了實現javascript腳本與C++庫之間的相互調用,Nodejs對V8引擎的api做了一層封裝,稱為NAPI。可以在Nodejs官網(https://nodejs.org/dist/latest-v20.x/docs/api/n-api.html)上查看各種NAPI接口定義說明。

可以看到,NAPI接口本身是C++語言實現的,這些接口可以幫助C++代碼創建JS變量,或訪問JavaScript運行環境中的JS變量與方法。

OpenHarmony中的NAPI

OpenAtom?OpenHarmony(以下簡稱“OpenHarmony”)應用層基于javascript語言開發,而系統框架層則基于C++語言。它們之間需要一個橋梁來實現兩種語言代碼之間的相互調用,這個橋梁就是NAPI。

這里可能有的小伙伴有疑問了:OpenHarmony的NAPI和NodeJs的NAPI是一回事嗎?應該說,OpenHarmony系統沿用了NAPI的接口定義形式,但每個接口的內部實現都進行了重寫。這是因為NAPI接口的本質是幫助C++程序去跟Javascript引擎交互,因此對于不同的引擎需要有不同的實現方式。當用戶調用了NAPI接口?napi_create_int64(),?對于Nodejs而言,它會去訪問V8引擎的api創建一個js的數字變量,而對于OpenHarmony,則是去訪問ArkUI框架自己的js引擎(ArkNativeEngine)。在OpenHarmony源碼中搜索?napi_create_int64()?方法,你會得到一份頭文件定義:third_party\node\src\js_native_api.h以及兩份不同的實現代碼:third_party\node\src\js_native_api_v8.ccfoundation\arkui\napi\native_engine\native_api.cppnative_api.cpp是OpenHarmony版本的NAPI實現,想了解內部細節的可以從這里入手:

創建一個簡單的NAPI工程

可以通過DevEco?Studio的Native?C++模板創建一個包含簡單NAPI?實現的樣例工程。

該工程自帶一個hello.cpp,實現了一個能夠被javascript代碼調用的add()方法。

下面我們就基于這個簡單的例子,探究一下NAPI框架的實現原理。

應用如何調用NAPI接口

應用代碼導入對應的so庫后,就可以調用該庫實現的接口。

這里我們注意到,導入日志庫時使用的名稱是"@ohos.hilog",應用代碼如果寫成??import?hilog?from?'libhilog.z.so'??其實也是可以成功導入的。實際上,ArkUI在運行時會將@ohos.hilog轉換為libhilog.z.so,然后到?/system/lib/module/?目錄下查找此庫并加載。系統實現的NAPI庫都放在/system/lib/module/目錄下,類似的:@ohos.wifiManager對應的是?/system/lib/module/libwifimanager.z.so;@ohos.deviceInfo?對應的是?/system/lib//module/libdeviceinfo.z.so

除了系統自帶的NAPI庫,應用也可以用C++開發自己的NAPI庫。上面例子中?import?testNapi?from?'libentry.so'?導入的就是應用自己實現的。應用開發的NAPI庫會隨著應用工程一起編譯打包到hap文件中,最終部署到/data目錄每個應用自己的文件夾下。

NAPI庫的導入原理

我們知道,應用的javascript代碼是由ArkUI的JS引擎解釋執行的。當JS引擎解讀?import?hilog?from?'@ohos.hilog';?這行代碼時,會通過dlopen()?將對應的libhilog.z.so加載到應用進程中。這一切是怎么做到的呢?每個應用進程在初始化時,都會創建一個引擎實例?ArkNativeEngineImpl,我們來看一下它的構造函數foundation\arkui\napi\native_engine\impl\ark\ark_native_engine_impl.cpp

也就是說,每個應用進程的JS引擎中,都注冊了一個"requireNapi"函數,當應用調用此方法時,JS引擎就會通過NAPI框架的moduleManager類去處理so庫的加載。moduleManager內部最終是找到了/system/lib/module下對應的so文件,并通過dlopen()的方式加載到應用進程中。想了解細節的小伙伴可以讀一下NativeModuleManager::LoadNativeModule()方法的內部實現。

這里可能會有個疑問:應用的javascript代碼中并沒有寫什么"requireNapi"的代碼,只有import?xxx,怎么觸發的導入處理函數?答案要到編譯后的js代碼中尋找。我們解開編譯后的hap包,找到ets文件對應的js文件:

可以看到,index.ets被編譯成index.js后,import關鍵字也被轉為了"requireNapi",這樣JS引擎在執行這行代碼時,就會去調用注冊的導入處理函數了。

C++庫如何實現JS方法

前面解決了JS?導C++庫的問題,下一步就是JS如何調用C++庫里的方法了。先說結論:一個C++方法能否被應用調用,取決與C++代碼有沒有將這個方法注冊到JS引擎。

我們來看看hello.cpp是如何注冊add方法的:

我們可以從下往上看這段代碼:首先是?RegisterEntryModule(void)?方法。這是C++向JS引擎進行NAPI模塊與方法注冊的起始代碼。注意這個方法前面有個編譯修飾符?"__attribute__((constructor))",它的作用是指導C++代碼的編譯,使得當so庫被加載到應用進程中時,RegisterEntryModule(void)?方法就會被自動調用到。該方法通過NAPI接口napi_module_register()?向JS引擎注冊了一個?napi_module。

然后是Init()方法。該方法實現了Add方法的注冊。也就是告訴JS引擎,將JS符號"add"?與C++方法"Add"?進行關聯映射。這樣后續當JS引擎解釋執行javascript代碼?"testNapi.add(2,?3)"時,就會找到C++?Add()方法的函數地址并調用。如下圖所示:

方法關聯調用的問題也解決了,最后就是JS運行環境與C++運行環境的相互切換了。當C++的Add方法被JS引擎調用到后,引擎會將javascript下發的參數變量傳遞給C++。所有從JS運行環境傳遞過來的變量都是用napi_value類型來表示的。需要通過NAPI接口轉為C++語言的變量類型。詳見下圖每行代碼的注釋:

napi_value不是一個具體的類型,它類似于void*,表示的是JS變量在JS引擎內部存儲區內的地址。需要通過對應的NAPI方法實現,例如:napi_get_value_int32()??---?js變量轉為c++整形napi_get_value_string_utf8()?---?js變量轉為c++字符串napi_get_value_bool()?---?js變量轉為c++布爾值

這些接口的具體用法和使用場景,可以參考NodeJs官方文檔(https://nodejs.org/dist/latest-v20.x/docs/api/n-api.html)

C++程序鏈接NAPI庫

OpenHarmony的NAPI接口實現都封裝在libace_napi.z.so中,C++程序編譯時需鏈接此庫。對于DevEco?Studio應用開發的cpp代碼,在對應的CMakeLists.txt中鏈接。該庫文件在SDK目錄下可以找到。

對于設備側開發,系統框架中的C++程序,則通過BUILD.gn文件定義依賴關系。

總結

NAPI是JavaScript與C++交互的橋梁。在OpenHarmony中,Javascript代碼在運行時由ArkUI的JS引擎解釋執行,C++代碼則通過NAPI接口訪問JS引擎中的Javascript上下文,從而實現與JS變量、方法之間的相互調用。

參考鏈接

以下是源碼倉庫地址

arkui_napi: Development framework for extending the JS Native Module | 原生模塊擴展開發框架

third_party_node: Third-party open-source software node | 三方開源軟件node

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/163772.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/163772.shtml
英文地址,請注明出處:http://en.pswp.cn/news/163772.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【python爬蟲】scrapy在pycharm 調試

scrapy在pycharm 調試 1、使用scrapy創建一個項目 scrapy startproject tutorial 2、在朋友pycharm中調試scrapy 2.1 通過文件run.py調試 在根目錄下新建一個文件run.py(與scrapy.cfg文件的同一目錄下), debug ‘run’即可 # -*- coding:utf-8 -*- from scrapy import c…

深入淺出理解libevent——2萬字總結

概述 libevent,libev,libuv都是c實現的異步事件庫,注冊異步事件,檢測異步事件,根據事件的觸發先后順序,調用相對應回調函數處理事件。處理的事件包括:網絡 io 事件、定時事件以及信號事件。這三個事件驅動著服務器的運…

數字人是真人嗎?

引言: 隨著科技的不斷進步,數字人作為一種新興技術正逐漸嶄露頭角。數字人是通過計算機生成的虛擬人物,具備逼真的外貌和行為,令人難以分辨其與真人的差異。本文將探討數字人是否可以被視為真人,并探索數字人技術在各個…

柯橋生活日語學習,打工人的日語你會嗎?

打工人在日語里有幾種說法: アルバイト 這是最常用的稱呼,直接對應中文的“打工”。 例句: 學生の頃はスーパーでアルバイトをしていた。(我學生時代在超市打過工。) バイト これはアルバイトの略稱でよく使われる。(這是アルバイト的簡稱,也很常用。) 例句: バイト先が決…

《第一行代碼:Android》第三版-2.4.1 if 語句

本文主要講解if語句,kotlin的if語句是可以有返回值的,就是if語句的最后一句話就是返回值。 /*** You can edit, run, and share this code.* play.kotlinlang.org*/fun main() {println("Hello, world!!!") val largelargerNumber(5,9) prin…

如何提高希音、亞馬遜、國際站店鋪流量轉化,自養號優勢及測評底層環境邏輯

隨著全球貿易數字化程度加快,尤其是跨境電商的發展日新月異,在外貿出口占比越來越高,在這其中,亞馬遜作為全球實力強勁的在線零售平臺之一,吸引了大量的優秀賣家。 而這也加劇了亞馬遜平臺的競爭程度,尤其…

HCIP數據通信——BGP協議

引言 我之前寫過一篇介紹ISIS的文章,我打算把BGP知識總結以后再做實驗。那么現在就講述一下BGP的一些特點和概念。 BGP特點 BGP屬于EGP(EGP也是BGP前身,指的是具體協議,被淘汰了成為了BGP),無類協議。 它…

C++(14):解決lambda生命期問題

C++(11):局部函數lambda_c++11 函數中定義函數-CSDN博客 中通過實例列舉了lambda使用過程中可能會有變量生命期問題。 C++14中可以通過重新定義變量,并轉移,解決這個問題: #include <iostream> using namespace std;class A { public:A(int data):m_data(data){cou…

繼承中:一般函數的virtual虛函數特性、析構函數的virtual虛函數特性

1、一般的同名函數 c規定&#xff0c;當一個成員函數被聲明為虛函數后&#xff0c;其派生類中的同名函數都自動成為虛函數。因此&#xff0c;在子類重新聲明該虛函數時&#xff0c;可以加&#xff0c;也可以不加&#xff0c;但習慣上每一層聲明函數時都加virtual,使程序更加清…

postgresql數據庫中update使用的坑

簡介 在數據庫中進行增刪改查比較常見&#xff0c;經常會用到update的使用。但是在近期發現update在oracle和postgresql使用卻有一些隱形區別&#xff0c;oracle 在執行update語句的時候set 后面必須跟著1對1的數據關聯而postgresql數據庫卻可以一對多&#xff0c;這就導致數據…

完整的工程項目管理流程是怎么樣的?

閱讀本文你將了解工程項目管理的完整流程&#xff1a;一、項目啟動階段&#xff1b;二、項目規劃階段&#xff1b;三、項目執行階段&#xff1b;四、項目收尾階段&#xff1b;五、項目總結與反饋。 這是一個工程項目管理的完整流程&#xff1a; 項目啟動階段&#xff1a;也就…

xlsxwriter.exceptions.FileCreateError: [Errno 13] Permission denied: ‘E:

xlsxwriter.exceptions.FileCreateError: [Errno 13] Permission denied: ‘E:\、、、、、’ 如果你嘗試了各種修改文件權限的方法都還不行的話 有可能是因為你打開了想要修改的文件&#xff0c;關閉就好啦

Android12 ROM定制導讀

一、前言 本專欄出現的原因: 沉淀自己,距離上一篇博客已經過去幾個月了,筆者最近工作上的事情非常忙,導致博文斷更了,今天忙里偷閑有一段短暫的時間,把這段時間遇到的問題準備整理一下,以文章的形式記錄下來。Android10的專欄也會慢慢更新。讓筆者最為感慨的就是Androi…

C語言分支限界法求解01背包問題

分支限界法是一種求解優化問題的算法&#xff0c;針對01背包問題&#xff0c;它可以通過在搜索過程中剪枝&#xff0c;減少搜索空間的大小&#xff0c;提高算法的效率。 具體來說&#xff0c;分支限界法會將當前狀態下的可行解集合分成若干個子集&#xff0c;每個子集代表一條…

Java特殊文件讀取案例Properties

代碼 package com.itheima.d1;import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; import java.util.Properties;public class Test3 {public static void main(String[] args) throws Exception {//目標&#xff1a;讀取屬性文件…

SpringBoot通過@Scheduled實現定時任務

Spring自帶的定時任務系統&#xff0c;使用注解時必須指定任意一個參數&#xff08;屬性&#xff09;&#xff1a;cron、fixedDelay或fixedRate&#xff1b; 1. 啟動類添加開啟注解 EnableScheduling 2. cron參數 /** * cron 一共可以有7個參數 以空格分開 其中年不是必須參…

java項目之品牌銀飾售賣平臺(ssm+vue)

項目簡介 主要功能包括首頁、個人中心、用戶管理、促銷活動管理、飾品管理、我的收藏管理、系統管理、訂單管理等。管理員模塊: 管理員可以查詢、編輯、管理每個用戶的信息和系統管理員自己的信息&#xff0c;同時還可以編輯、修改、查詢用戶賬戶和密碼&#xff0c;以及對系統…

EMG肌肉電信號處理合集(三)

本文主要展示常見的肌電信號預處理的實現&#xff0c;開發環境為matlab。 目錄 1 肌電信號低通&#xff0c;高通&#xff0c;帶通濾波 2 去除DC 0階偏置&#xff0c;1階偏置 3 全波整流 4 信號降采樣 5 linear envolope / butterworth 低通濾波器 1 肌電信號低通&#xf…

pdf.js插件怎么控制工具欄的顯示與隱藏

最近做了一個需求&#xff0c;需要實現pdf文件的預覽&#xff0c;但是只是提供預覽功能&#xff0c;不需要展示相關的工具欄&#xff0c;所以需要把工具欄隱藏掉。我用的插件是pdf.js 官網地址&#xff1a;http://mozilla.github.io/pdf.js/ 中文文檔地址&#xff1a;https://…

鄰趣連接力:如何無代碼集成CRM、電商平臺和營銷系統,提升廣告推廣效率

連接即服務&#xff1a;鄰趣無代碼集成方法 傳統的電商系統集成過程需要大量的時間和資源進行API開發&#xff0c;這不僅耗時耗力&#xff0c;還需要專業的技術團隊支持。然而&#xff0c;鄰趣通過提供一種無需API開發的連接方法&#xff0c;極大地簡化了整個集成過程。商家只…