鴻蒙應(yīng)用Native SDK C++ (JNI)開(kāi)發(fā)實(shí)戰(zhàn)
想了解更多內(nèi)容,請(qǐng)?jiān)L問(wèn):
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)
什么是鴻蒙Native SDK
- Native SDK是一套工具,使您能夠在 HarmonyOS 應(yīng)用中使用 C 和 C++ 代碼,并提供眾多平臺(tái)庫(kù),Native SDK 可能不適合大多數(shù)編程初學(xué)者,這些初學(xué)者只需使用 Java /JS代碼和框架 API 開(kāi)發(fā)應(yīng)用。然而,如果需要實(shí)現(xiàn)以下一個(gè)或多個(gè)目標(biāo),那么 Native SDK 就能派上用場(chǎng):
- 可以進(jìn)一步提升設(shè)備性能,以降低延遲或運(yùn)行游戲、物理模擬等計(jì)算密集型應(yīng)用。
- 重復(fù)使用您自己或其他開(kāi)發(fā)者的 C 或 C++ 庫(kù)。
HarmonyOS提供了一些圖形圖像、日志、媒體等相關(guān)的Native API。
1 鴻蒙NDK開(kāi)發(fā)環(huán)境準(zhǔn)備
1.1 開(kāi)發(fā)工具DevEco studio安裝
- 確認(rèn)安裝好開(kāi)發(fā)工具DevEco studio,版本需要支持Native SDK的
- 本文章的版本是 DevEco studio 2.2.0.200
- 工具到鴻蒙官網(wǎng)下載,如果已經(jīng)沒(méi)有2.2版本可以到hmxt.club下載
1.2 設(shè)置安裝Native SDK(NDK)
第一次安裝,正常默認(rèn)安裝了NDK,如需要修改版本,點(diǎn)擊左下角Configure 進(jìn)入設(shè)置。

如果已經(jīng)打開(kāi)項(xiàng)目則選中DevEco studio的菜單File->Settings。

2 第一個(gè)鴻蒙應(yīng)用NDK示例
2.1 創(chuàng)建第一個(gè)HamonyOs Native C++項(xiàng)目

2.2 Native C++項(xiàng)目的文件分析
2.2.1 build.gradle 鴻蒙app項(xiàng)目配置
- 路徑entry/build.gradle
- 配置編譯的CPU對(duì)應(yīng)的架構(gòu) arm64-v8a
- 配置C++項(xiàng)目編譯用的cmake文件 src/main/cpp/CMakeLists.txt
- 配置C++ 編譯參數(shù)-指定C++ 版本 -std=c++17

2.2.2 CMakeList.txt (C++項(xiàng)目文件)
- 路徑 entry/src/main/cpp/CMakeList.txt
- add_library(hello SHARED hello.cpp)
- 輸出為動(dòng)態(tài)庫(kù) 指定c++源碼文件
- target_link_libraries(hello libhilog_ndk.z.so)
- 設(shè)定項(xiàng)目依賴的庫(kù)

2.2.3 ndk c++源碼(jni.h)
- 路徑 entry/src/main/cpp/hello.cpp
- 通過(guò)函數(shù)名稱對(duì)應(yīng)java調(diào)用
- 引用jni.h與java交互
- #include <jni.h>
- #include <string>
- #include <Hilog/log.h>
- extern "C"
- JNIEXPORT jstring JNICALL
- Java_com_example_myapplication_slice_MainAbilitySlice_stringFromJNI(JNIEnv* env, jobject obj) {
- std::string hello = "Hello from JNI C++ codes";
- int len = hello.size();
- jchar res[len];
- for (int i = 0; i < len; i++) {
- res[i] = (jchar)hello[i];
- }
- return env->NewString(res, len);
- }
2.2.4 ndk java源碼
- 文件名 MainAbilitySlice.java
- System.loadLibrary(“hello”);
- 導(dǎo)入c++動(dòng)態(tài)庫(kù)
- C++函數(shù)與java函數(shù)綁定
假定java函數(shù)名為 public native String stringFromJNI(); 那 java_命名空間_類名_函數(shù) = C語(yǔ)言函數(shù) ,所以c++函數(shù)名為Java_com_example_myapplication_slice_MainAbilitySlice_stringFromJNI(JNIEnv* env, jobject obj),后面的參數(shù)對(duì)應(yīng)java的線程環(huán)境和調(diào)用stringFromJNI函數(shù)的對(duì)象。

3 Native SDK (NDK)原生的C++接口分析
- 在上一章創(chuàng)建項(xiàng)目后,本章分析下c++能夠調(diào)用哪些接口、c++程序的編譯流程、庫(kù)和編譯工具所在路徑。
3.1 官方Native API參考
官方直接提供了api的參考,目前鴻蒙自身的接口支持得還不多。

3.2 Native API支持得標(biāo)準(zhǔn)庫(kù)
3.3 分析鴻蒙native SDK 工具和庫(kù)
在DevEco studio的菜單File->Project Structure進(jìn)入可以看到,項(xiàng)目引用的ndk的路徑,并且可以設(shè)置修改ndk的版本,本文選擇了2.2.0.1版本的ndk。

進(jìn)入ndk目錄可以看到:

其中l(wèi)lvm目錄下是編譯工具鏈,包含編譯工具和C/C++ 庫(kù),進(jìn)入llvm/bin后可以看到編譯的工具是clang 和clang++。

sysroot是系統(tǒng)庫(kù)和頭文件路徑。


通過(guò)頭文件和庫(kù)文件可以看到OpenGLES 三維渲染 OpenSLES 原始音頻 也是支持的,原生音頻opensles已測(cè)可以正常調(diào)用,OpenGLES 三維渲染沒(méi)有調(diào)用測(cè)試,但是通過(guò)鴻蒙ndk工具已經(jīng)成功編譯開(kāi)源的三維引擎。
NDK配置 cmake說(shuō)明
JNI實(shí)現(xiàn)C++與java交互
4 JNI入門(mén)
4.1 什么是JNI
- JNI 是指 Java 原生接口。它定義了Java編譯的字節(jié)碼與原生代碼(使用 C/C++ 編寫(xiě))互動(dòng)的方式。JNI 不依賴于供應(yīng)商,支持從動(dòng)態(tài)共享庫(kù)加載代碼。
4.2 JNI開(kāi)發(fā)原則
- 盡可能減少跨 JNI 層傳遞資源的次數(shù)
- 盡可能避免JAVA與C++異步通信
- 盡可能減少需要接觸 JNI 或被 JNI 接觸的線程數(shù)
- 將接口代碼保存在少量易于識(shí)別的 C++ 和 Java 源位置,以便將來(lái)進(jìn)行重構(gòu)
4.3 JavaVM 和 JNIEnv
JavaVM
- Java語(yǔ)言的執(zhí)行環(huán)境是Java虛擬機(jī)(JVM),每個(gè)JVM虛擬機(jī)都在本地環(huán)境中有一個(gè)JavaVM結(jié)構(gòu)體,JavaVM是Java虛擬機(jī)在JNI層的代表,JNI全局僅僅有一個(gè)JavaVM結(jié)構(gòu)中封裝了一些函數(shù)指針(或叫函數(shù)表結(jié)構(gòu)),JavaVM中封裝的這些函數(shù)指針主要是對(duì)JVM操作接口。
JNIEnv
- 每個(gè)線程對(duì)應(yīng)一個(gè)JNIEnv結(jié)構(gòu)
- JNIEnv 提供了大部分 JNI 函數(shù)。原生函數(shù)第一個(gè)參數(shù)都是JNIEnv
- 您無(wú)法在線程之間共享 JNIEnv
- 可以使用 AttachCurrentThread() 或 AttachCurrentThreadAsDaemon() 函數(shù)附加通過(guò) pthread_create() 或 std::thread 啟動(dòng)的線程。
- 在附加之前,線程不包含任何 JNIEnv,也無(wú)法調(diào)用 JNI
- JNI 附加的線程在退出之前必須調(diào)用 DetachCurrentThread()
4.4 jclass、jmethodID 和 jfieldID
- 分別對(duì)應(yīng)java中的類、成員函數(shù)和成員變量。
- 傳遞給原生方法的每個(gè)參數(shù),以及 JNI函數(shù)返回的幾乎每個(gè)對(duì)象都屬于“局部引用”。這意味著,局部引用在當(dāng)前線程中的當(dāng)前原生方法運(yùn)行期間有效。在原生方法返回后,即使對(duì)象本身繼續(xù)存在,該引用也無(wú)效。
- 獲取非局部引用的唯一方法是通過(guò) NewGlobalRef 和 NewWeakGlobalRef 函數(shù)。
JNI CMake ninja NDK c++ java的關(guān)系

結(jié)束
更多jni接口參數(shù)可以直接查看java的手冊(cè)
更多內(nèi)容可以觀看51cto學(xué)堂上的課程《鴻蒙Native SDK JNI C++開(kāi)發(fā)入門(mén)和實(shí)戰(zhàn)-示例擴(kuò)展支持lua腳本》,也可以關(guān)注我后發(fā)發(fā)的文章。
想了解更多內(nèi)容,請(qǐng)?jiān)L問(wèn):
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)