初識 JNI

初識 JNI

JNI, Java Native Interface(Java本地接口).

概述

JNI 是用於和本地 C 代碼進行交互操做的API。實際上能夠經過許多語言編寫,如C++、C#,本質上 Java 調用的是 dll/so 庫 。此處說是和本地 C 代碼互操做是由於 JNI API的支持。JNI API針對的是 C 語言,而不是Java。java

JNI 的出現的緣由有一些是爲了彌補一些沒法經過 Java 平臺實現的功能。好比 Windows 的註冊表,這是 Windows 獨有的東西,Java 並無現成能夠操做它的 API。後端

也有一些緣由是非 Java 語言編寫的代碼已經通過了大量的測試,無需從新用 Java 實現一套。bash

固然,最初也有 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 中定義函數,函數名有以下要求:code

  • 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);
相關文章
相關標籤/搜索