【Android NDK 开发】Visual Studio 2019 使用 CMake 开发 JNI 动态库 ( 动态库编译配置 | JNI 头文件导入 | JNI 方法命名规范 )
文章目錄
- I . JNI 與 NDK 區別
- II . Visual Studio 編譯動態庫
- III. 配置 導入 jni.h 頭文件
- IV . IntelliJ IDEA Community Edition 創建 Java 項目
- V . Java 定義的 Native 方法
- VI . C++ 中實現上面定義的 Native 方法
- VII . CMake 項目生成 dll 動態庫
- VIII . Java 中加載調用動態庫
- IX . 使用 javah 工具生成 C++ 中需要實現的 Native 方法 ( 僅做參考 )
- X . 總結
I . JNI 與 NDK 區別
1 . JNI 簡介 : JNI 是一套框架 , 能夠讓開發者在 Java 中調用 C / C++ 代碼 , JNI 范圍較廣 , 凡是可以運行 Java 代碼的地方 ( 如 Linux , UNIX , Windows , Android 等平臺 ) , 都可以通過 JNI 接口 調用 C/C++ 代碼 ;
NDK 只是 Android 平臺的 JNI 規范 , 屬于 JNI 的一個分支 ;
2 . NDK 簡介 : NDK 是 Android 提供的開發工具包 , 其中包含了
① Android 平臺的交叉編譯器 ;
② Android 平臺的一系列動態庫 及 靜態庫 ;
本篇博客只介紹 JNI , 不涉及 NDK 相關概念;
II . Visual Studio 編譯動態庫
前提 : 需要搭建 Visual Studio 的 CMake 開發環境 ;
【Visual Studio】Visual Studio 2019 社區版 CMake開發環境安裝 ( 下載 | 安裝相關組件 | 創建編譯執行項目 | 錯誤處理 )
在 Visual Studio 2019 中創建 CMake 項目 :
① 創建項目 : 在歡迎界面中 , 點擊創建新項目 ;
② 選擇 CMake 項目 , 點擊下一步 ;
③ 設置項目名稱 , 選擇項目位置 , 點擊 “創建” 按鈕 ;
④ 項目創建完畢 ;
⑤ 配置 CMakeList.txt 配置文件 , 設置生成動態庫選項 ;
默認生成的是可執行文件 , 但是此處我們要生成動態庫 , 因此將默認的配置注釋掉 ;
生成動態庫的配置格式 : add_library( 庫名稱 庫類型 包含的源文件 ) ;
# CMakeList.txt: 009_Cmake 的 CMake 項目,在此處包括源代碼并定義 # 項目特定的邏輯。 # cmake_minimum_required (VERSION 3.8)# 設置生成 動態庫 # 配置格式是 : 動態庫名稱 動態庫標識( SHARED ) 包含的源文件( 如果有多個就寫多個 ) add_library( 009_Cmake SHARED 009_Cmake.cpp 009_Cmake.h )# 將源代碼添加到此項目的可執行文件。 #add_executable (009_Cmake "009_Cmake.cpp" "009_Cmake.h")# TODO: 如有需要,請添加測試并安裝目標。⑥ 生成動態庫 : 使用 “Ctrl + Shift + B” 快捷鍵 , 編譯項目 , 即可生成動態庫 ;
⑦ 查看動態庫 : 在項目的 “項目根目錄\out\build\x64-Debug\009_Cmake” 目錄下有生成的 009_Cmake.dll 動態庫 , 這是個 Windows 動態庫 ;
動態庫生成目錄 : Y:\002_WorkSpace\002_VS\009_Cmake\out\build\x64-Debug\009_Cmake
III. 配置 導入 jni.h 頭文件
1 . jni.h 頭文件位置 : JNI 的頭文件在 JDK 的安裝目錄中 的 include 文件夾下 ;
D:\Program Files\Java\jdk1.8.0_221\include
D:\Program Files\Java\jdk1.8.0_221\include\win32
2 . 將 JNI 頭文件配置到 CMake 中 :
#配置 JNI 頭文件 include_directories("D:/Program Files/Java/jdk1.8.0_221/include") include_directories("D:/Program Files/Java/jdk1.8.0_221/include/win32")配置完后的 CMakeList.txt 文件 :
# CMakeList.txt: 009_Cmake 的 CMake 項目,在此處包括源代碼并定義 # 項目特定的邏輯。 # cmake_minimum_required (VERSION 3.8)#配置 JNI 頭文件 include_directories("D:/Program Files/Java/jdk1.8.0_221/include") include_directories("D:/Program Files/Java/jdk1.8.0_221/include/win32")# 設置生成 動態庫 # 配置格式是 : 動態庫名稱 動態庫標識( SHARED ) 包含的源文件( 如果有多個就寫多個 ) add_library( 009_Cmake SHARED 009_Cmake.cpp 009_Cmake.h )# 將源代碼添加到此項目的可執行文件。 #add_executable (009_Cmake "009_Cmake.cpp" "009_Cmake.h")# TODO: 如有需要,請添加測試并安裝目標。3 . 導入 JNI 頭文件 : 使用 #include <jni.h> 導入JNI頭文件 , 點擊生成 , 沒有報錯 , 說明導入成功 ;
// 009_Cmake.cpp: 定義應用程序的入口點。//導入 JNI 的頭文件 , 該頭文件在 D:/Program Files/Java/jdk1.8.0_221/include 目錄中 // JDK 的安裝目錄 , 每個人的安裝目錄可能不一致 #include <jni.h>IV . IntelliJ IDEA Community Edition 創建 Java 項目
不做 J2EE 開發 , 只是跑一些 Java , Kotlin 項目 , 使用社區版 ( Community ) 即可 ;
IntelliJ IDEA 創建 Java 項目 :
① 在 IntelliJ IDEA 的歡迎界面中 , 點擊創建新工程 " Create New Project " 按鈕 :
② 選擇 Java 選項卡 , 然后點擊 " Next " 按鈕 ;
③ 選擇一個 模板 :
④ 設置項目參數 : 在最后一個對話框中設置 工程名稱 ( Project name ) , 選擇工程位置 ( Project location ) , 以及 包名 ( Base package ) ;
V . Java 定義的 Native 方法
在 Java 項目的代碼中 , 定義 Native 方法 , 包名為 " kim.hsl.jni " , 類名為 " Main " ;
定義的 Native 方法如下 :
/*** 定義一個 Native 方法* @param i* @param s*/public native void jniTest(int i, String s);完整的 Java 入口類代碼如下 :
package kim.hsl.jni;public class Main {public static void main(String[] args) {}/*** 定義一個 Native 方法* @param i* @param s*/public native void jniTest(int i, String s); }VI . C++ 中實現上面定義的 Native 方法
1 . C++ 中實現上述 Java 中聲明的 Native 方法 : 實現的 JNI 方法如下 , 下面會逐條講解每個 關鍵字 或 格式的含義 ;
extern "C" JNIEXPORT void JNICALL Java_kim_hsl_jni_Main_jniTest(JNIEnv* env, jobject instance, jint i, jstring s_) {const char* s = env->GetStringUTFChars(s_, 0);// 打印傳入的 兩個參數printf("Java_kim_hsl_jni_Main_jniTest : %d , %s\n", i , s);env->ReleaseStringUTFChars(s_, s); }2 . C++ 兼容 C 語言設置 : extern “C” , 作用是在 C++ 代碼中 , 兼容 C 代碼 ;
① 如果是在 C++ 文件 ( .cpp 后綴源碼 ) 中實現 Native 方法 , 需要兼容 C 語言 ;
② 如果在 C 文件 ( .c 后綴源碼 ) 中 , 則不用添加該選項 ;
3 . JNI 方法基本格式 : JNIEXPORT 返回值類型 JNICALL 方法名 ( 參數列表 ) ;
4 . 方法名規范 : Java_包名_類名_方法名 , 如包名為 " kim.hsl.jni " , 類名為 " Main " , 方法名為 " jniTest " , 那么 C/C++ 中對應的 Native 方法名為 " Java_kim_hsl_jni_Main_jniTest " ;
5 . 參數列表 : 分析該參數列表 ( JNIEnv* env, jobject instance, jint i, jstring s_ ) ;
① JNIEnv* env : 第一個參數必定是 JNI 環境參數 , 即 JNIEnv 類型的 指針 ;
② jobject instance : 第二個參數必定是 定義 Native 方法的 Java 類對象 ;
③ jint i, jstring s_ : 從第三個開始就是定義的 Java 中的 Native 方法的參數 , 注意要使用 java 的替代數據類型 ;
VII . CMake 項目生成 dll 動態庫
1 . 在上面實現了 JNI 對應的 Native 方法 :
2 . 配置 CMakeList.txt 配置文件 , 設置生成動態庫選項 ;
默認生成的是可執行文件 , 但是此處我們要生成動態庫 , 因此將默認的配置注釋掉 ;
生成動態庫的配置格式 : add_library( 庫名稱 庫類型 包含的源文件 ) ;
# CMakeList.txt: 009_Cmake 的 CMake 項目,在此處包括源代碼并定義 # 項目特定的邏輯。 # cmake_minimum_required (VERSION 3.8)# 設置生成 動態庫 # 配置格式是 : 動態庫名稱 動態庫標識( SHARED ) 包含的源文件( 如果有多個就寫多個 ) add_library( 009_Cmake SHARED 009_Cmake.cpp 009_Cmake.h )# 將源代碼添加到此項目的可執行文件。 #add_executable (009_Cmake "009_Cmake.cpp" "009_Cmake.h")# TODO: 如有需要,請添加測試并安裝目標。3 . 生成動態庫 : 使用 “Ctrl + Shift + B” 快捷鍵 , 編譯項目 , 即可生成動態庫 ;
4 . 查看動態庫 : 在項目的 “項目根目錄\out\build\x64-Debug\009_Cmake” 目錄下有生成的 009_Cmake.dll 動態庫 , 這是個 Windows 動態庫 ;
動態庫生成目錄 : Y:\002_WorkSpace\002_VS\009_Cmake\out\build\x64-Debug\009_Cmake\009_Cmake.dll
" Y:\002_WorkSpace\002_VS\009_Cmake\out\build\x64-Debug\009_Cmake\009_Cmake.dll " 這個目錄很重要 , 一會兒還要在 Java 代碼中通過該絕對路徑加載動態庫 ;
VIII . Java 中加載調用動態庫
1 . 操作步驟 : Java 中首先要加載動態庫 , 然后才能調用動態庫中實現的 Native 方法 ;
① 加載動態庫 :
static {//Visual Studio 中生成的 DLL 動態庫路徑是// Y:\002_WorkSpace\002_VS\009_Cmake\out\build\x64-Debug\009_Cmake\009_Cmake.dll// 直接使用該絕對路徑加載動態庫即可System.load("Y:\\002_WorkSpace\\002_VS\\009_Cmake\\out\\build\\x64-Debug\\009_Cmake\\009_Cmake.dll");}② 調用 Native 方法 :
public static void main(String[] args) {//打印結果 : Java_kim_hsl_jni_Main_jniTest : 1 , Hello JNIjniTest(1 , "Hello JNI");}2 . 完整 Java 代碼 :
package kim.hsl.jni;public class Main {static {//Visual Studio 中生成的 DLL 動態庫路徑是// Y:\002_WorkSpace\002_VS\009_Cmake\out\build\x64-Debug\009_Cmake\009_Cmake.dll// 直接使用該絕對路徑加載動態庫即可System.load("Y:\\002_WorkSpace\\002_VS\\009_Cmake\\out\\build\\x64-Debug\\009_Cmake\\009_Cmake.dll");}public static void main(String[] args) {//打印結果 : Java_kim_hsl_jni_Main_jniTest : 1 , Hello JNIjniTest(1 , "Hello JNI");}/*** 定義一個 Native 方法* @param i* @param s*/public static native void jniTest(int i, String s); }3 . 執行結果 :
Java_kim_hsl_jni_Main_jniTest : 1 , Hello JNIIX . 使用 javah 工具生成 C++ 中需要實現的 Native 方法 ( 僅做參考 )
上面根據 Java_包名_類名_方法名 的方式比較繁瑣 , 容易出錯 , Java 中提供的 javah 工具 , 專門用于生成 實現 Native 方法的頭文件 ;
1 . 相關目錄說明 :
① Java 文件絕對路徑 : Y:\002_WorkSpace\003_IDEA\001_JNI_Hello\src\kim\hsl\jni\Main.java ;
② javah 命令執行路徑 : Y:\002_WorkSpace\003_IDEA\001_JNI_Hello\src\ ;
③ 需要進入的目錄 : 在命令行工具中 , 進入 javah 命令執行路徑 , 不要進錯目錄 ;
2 . 執行 Javah 命令 : 使用 javah -o Main.h kim.hsl.jni.Main 命令 , 生成對應的 C / C++ 頭文件 , 該頭文件中定義有要實現的 Native 方法聲明 ;
① 指定輸出文件 : 其中 -o Main.h 用于指定生成的目標文件 , 即在當前執行命令的目錄生成 Main.h 頭文件 ;
② 指定源文件 : kim.hsl.jni.Main 用于指定要生成的參考類文件 ;
3 . 查看生成 Main.h 頭文件 :
生成的 Main.h 頭文件 :
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class kim_hsl_jni_Main */#ifndef _Included_kim_hsl_jni_Main #define _Included_kim_hsl_jni_Main #ifdef __cplusplus extern "C" { #endif /** Class: kim_hsl_jni_Main* Method: jniTest* Signature: (ILjava/lang/String;)V*/ JNIEXPORT void JNICALL Java_kim_hsl_jni_Main_jniTest(JNIEnv *, jclass, jint, jstring);#ifdef __cplusplus } #endif #endifX . 總結
1 . 創建 Java 代碼 : IntelliJ IDEA Community 中創建 Java 項目 , 并定義 Native 方法 ;
/*** 定義一個 Native 方法* @param i* @param s*/public static native void jniTest(int i, String s);2 . C++ 實現 Native 方法 : 在 Visual Studio Community 2019 中創建 CMake 項目 , 使用 C++ 開發 , 實現上面 Java 中聲明的 Native 方法 , 并在 CMake 中配置生成動態庫 ;
① C++ 代碼 :
// 009_Cmake.cpp: 定義應用程序的入口點。//導入 JNI 的頭文件 , 該頭文件在 D:/Program Files/Java/jdk1.8.0_221/include 目錄中 // JDK 的安裝目錄 , 每個人的安裝目錄可能不一致 #include <jni.h>//C++ 中實現 Java 的 Native 方法//JNI 方法格式 : // extern "C" : 如果是在 C++ 文件 ( .cpp 后綴源碼 ) 中實現 Native 方法 , 需要兼容 C 語言 // 如果在 C 文件 ( .c 后綴源碼 ) 中 , 則不用添加該選項 // JNIEXPORT 返回值類型 JNICALL 方法名 ( 參數列表 ) // 方法名規范 : Java_包名_類名_方法名 // 參數列表 : // 第一個參數必定是 JNI 環境參數 , 即 JNIEnv 類型的 指針 // 第二個參數必定是 定義 Native 方法的 Java 類對象 // 從第三個開始就是定義的 Java 中的 Native 方法的參數 , 注意要使用 java 的替代數據類型extern "C" JNIEXPORT void JNICALL Java_kim_hsl_jni_Main_jniTest(JNIEnv* env, jobject instance, jint i, jstring s_) {const char* s = env->GetStringUTFChars(s_, 0);// 打印傳入的 兩個參數printf("Java_kim_hsl_jni_Main_jniTest : %d , %s\n", i , s);env->ReleaseStringUTFChars(s_, s); }② CMake 動態庫配置 :
# CMakeList.txt: 009_Cmake 的 CMake 項目,在此處包括源代碼并定義 # 項目特定的邏輯。 # cmake_minimum_required (VERSION 3.8)#配置 JNI 頭文件 include_directories("D:/Program Files/Java/jdk1.8.0_221/include") include_directories("D:/Program Files/Java/jdk1.8.0_221/include/win32")# 設置生成 動態庫 # 配置格式是 : 動態庫名稱 動態庫標識( SHARED ) 包含的源文件( 如果有多個就寫多個 ) add_library( 009_Cmake SHARED 009_Cmake.cpp 009_Cmake.h )# 將源代碼添加到此項目的可執行文件。 #add_executable (009_Cmake "009_Cmake.cpp" "009_Cmake.h")# TODO: 如有需要,請添加測試并安裝目標。3 . Java 加載 動態庫 : 通過動態庫的絕對路徑加載該動態庫 , 并執行 ;
package kim.hsl.jni;public class Main {static {//Visual Studio 中生成的 DLL 動態庫路徑是// Y:\002_WorkSpace\002_VS\009_Cmake\out\build\x64-Debug\009_Cmake\009_Cmake.dll// 直接使用該絕對路徑加載動態庫即可System.load("Y:\\002_WorkSpace\\002_VS\\009_Cmake\\out\\build\\x64-Debug\\009_Cmake\\009_Cmake.dll");}public static void main(String[] args) {//打印結果 : Java_kim_hsl_jni_Main_jniTest : 1 , Hello JNIjniTest(1 , "Hello JNI");}/*** 定義一個 Native 方法* @param i* @param s*/public static native void jniTest(int i, String s); }關于項目兩個工程的說明 , 相關的路徑有可能改變 , 如 CMake 中配置 jni.h 頭文件路徑 , Java 中加載 VS 中生成的動態庫路徑 , 注意要修改成自己的項目路徑 ;
總結
以上是生活随笔為你收集整理的【Android NDK 开发】Visual Studio 2019 使用 CMake 开发 JNI 动态库 ( 动态库编译配置 | JNI 头文件导入 | JNI 方法命名规范 )的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【C++ 语言】智能指针 引入 ( 内存
- 下一篇: 【Android NDK 开发】JNI