初識 JNI
JNI, Java Native Interface(Java本地接口).
概述
JNI 是用於和本地 C 代碼進行交互操作的API。實際上可以通過許多語言編寫,如C++、C#,本質上 Java 調用的是 dll/so 庫 。此處說是和本地 C 代碼互操作是因爲 JNI API的支持。JNI API針對的是 C 語言,而不是Java。
JNI 的出現的原因有一些是爲了彌補一些無法通過 Java 平臺實現的功能。比如 Windows 的註冊表,這是 Windows 獨有的東西,Java 並沒有現成可以操作它的 API。
也有一些原因是非 Java 語言編寫的代碼已經經過了大量的測試,無需重新用 Java 實現一套。
當然,最初也有 JVM 運行的比較慢的原因,但隨着 JVM 的不斷髮展,Java編寫的代碼有時已經不遜於一些 C/C++ 的代碼了。
JNI 有着上述的一些好處,隨着而來的是它會帶來相應語言的缺點,
比如會引入 C 語言的無效指針造成的內存覆寫問題等等。
所以說 JNI 實際上使用範圍相對較窄,Web 後端方面用的比較少,安卓端用的相對更多一點。
示例 Hello World
以下爲一個簡單的示例
Java 部分
首先在在 Java 中聲明 dll/so 庫中定義的函數。
聲明通過 native
關鍵字標識,提醒編譯器該方法在外部定義。
// 爲了簡單,此處沒有package
public class HelloNative {
public static native void greeting();
}
C 部分
然後在 C 中定義函數,函數名有如下要求:
- Java_包名_類名_方法名 。(其中的.號都要改爲 _下劃線)
- 如果類名中含有非 ASCII 碼值,或說大於
\uoo7F
的 Unicode 字符,用_0xxxx
來代替。
(xxxx
是該字符的Unicode值的4個十六進制數序列) - 方法重載需要在名稱後加兩個下劃線,後再加上已編碼的類型。
爲了避免在函數定義時候出錯,Java 提供了 javah
工具完成函數名的編寫操作。javah
是通過類文件來生成相應的文件的,所以源代碼必須要先編譯纔可以。
javah HelloNative
通過如上命令,會生成一個 HelloNative.h 的文件,這個文件包含了 greeting()
方法的聲明。
複製該文件,改爲 .c 文件,去掉一些不需要的東西,然後包含 #include "HelloNative.h"
即可。
然後將方法聲明改爲方法實現,在方法實現中編寫具體的代碼。
JNIEXPORT void JNICALL Java_HelloNative_greeting (JNIEnv* env, jclass cl) {
printf("Hello Native World!");
}
之後就是編譯代碼了,此處僅粘貼個人測試過的命令,爲 Windows 上的 MinGW64 上的 gcc 。
gcc -I "jdk/include" -I "jdk/include/win32" -D __int64="long long" -shared -o HelloNative.dll HelloNative.c
關於 -D __int64="long long"
參數的說明:
Windows 上的 jni_md.h
含有聲明 typedef __int64 jlong;
,它專用於 cl 的。如果使用 gcc 需要設置 -D __int64="long long"
或者也可以修改此文件,如:
#ifdef __GNUC__
typedef long long jlong;
#else
typedef __int64 jlong;
#endif
調用
創建 HelloNativeTest
類以供測試:
public class HelloNativeTest {
static {
// 此處不需要 dll/so 後綴,系統會自動根據系統不同換後綴
System.loadLibrary("HelloNative");
}
public static void main(String[] args){
HelloNative.greeting();
}
}
記住這裏打印的消息是通過 printf
打印的,不是 java 的代碼打印的。
注:
一些本地代碼的共享庫必須先運行初始化代碼,可以將初始化代碼放置到JNI_OnLoad方法中,如果提供該方法,則虛擬機關
閉時會調用JNI_OnUnload方法,原型如下:
jint JNI_OnLoad(JavaVM* vm, void* reserved); //返回它所需的虛擬機的最低版本,如JNI_VERSION_1_2
void JNI_OnUnload(JavaVM* vm, void* reserved);