初識 JNI

初識 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);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章