你們都知道Java是跨平臺的,能夠說Java的出現帶給咱們巨大的方便。設想若是咱們的應用中須要訪問到特定的設備,甚至是僅符合公司內部信息交互規範的設備,或某個特定的操做系統纔有的特性,用Java來作顯然是十分不方便的,面對這些問題,Sun公司在 JDK1.0 中就定義了 JNI 規範,它規定了 Java 應用程序對本地方法的調用規則。java
我將詳細說明在 Linux 平臺下如何實現本地共享庫與 Java 協同工做,老規矩以Hello world爲例。linux
定義一個 Java 類 -- Hello類windows
public class Hello { static { try { // 此處即爲本地方法所在連接庫名 System.loadLibrary("hello"); } catch(UnsatisfiedLinkError e) { System.err.println( "Cannot load hello library:\n " + e.toString() ); } } public Hello() { } // 聲明的本地方法 public native void SayHello(String strName); }
這裏有兩個注意事項:函數
首先:爲要使用的每一個本地方法編寫本地方法聲明,只是必須指定 native 關鍵字,以下所示:測試
public native void SayHello(String strName);
其次:必須顯式地加載本地代碼庫。咱們需在類的靜態塊中加載這個庫(靜態庫在類加載時候就會調用)ui
如今咱們來編輯hello.java以生成hello.class文件。this
生成本地連接庫編碼
要爲以上定義的類生成 Java 本地接口頭文件,需使用 javah,Java 編譯器的 javah 功能將根據 Hello 類生成必要的聲明,此命令將生成 Hello.h 文件操作系統
生成的 Hello.h 文件 內容以下所示:指針
#include /* Header for class Hello */ #ifndef _Included_Hello #define _Included_Hello #ifdef __cplusplus extern "C" { #endif /* * Class: Hello * Method: SayHello * Signature: (Ljava/lang/String;)V */ JNIEXPORT void JNICALL Java_Hello_SayHello (JNIEnv *, jobject, jstring); #ifdef __cplusplus } #endif #endif
在與 Hello.h 相同的路徑下建立一個 CPP 文件 Hello.cpp
內容以下:
#include "Hello.h" #include // 與 Hello.h 中函數聲明相同 JNIEXPORT void JNICALL Java_Hello_SayHello (JNIEnv * env, jobject arg, jstring instring) { // 從 instring 字符串取得指向字符串 UTF 編碼的指針 const jbyte *str = (const jbyte *)env->GetStringUTFChars( instring, JNI_FALSE ); printf("Hello,%s\n",str); // 通知虛擬機本地代碼再也不須要經過 str 訪問 Java 字符串。 env->ReleaseStringUTFChars( instring, (const char *)str ); return; }
這裏有三個參數,下面講一下參數用法:
(1)全部的 JNI 調用都使用了 JNIEnv * 類型的指針,習慣上在 CPP 文件中將這個變量定義爲 evn,它是任意一個本地方法的第一個參數。env 指針指向一個函數指針表,在 VC 中能夠直接用"->"操做符訪問其中的函數。
(2)jobject 指向在此 Java 代碼中實例化的 Java 對象 LocalFunction 的一個句柄,至關於 this 指針。
(3)第三個參數就是本地調用中有 Java 程序傳進的參數,本例中只有一個 String 型參數。 對於字符串型參數,由於在本地代碼中不能直接讀取 Java 字符串,而必須將其轉換爲 C /C++ 字符串或 Unicode。
編譯生成共享庫。
使用 GCC 時 , 必須通知編譯器在何處查找此 Java 本地方法的支持文件,而且顯式通知編譯器生成位置無關的代碼,在個人環境中按以下過程編譯:
gcc -I/home/jbuilder/jdk1.3.1/include -I/home/jbuilder/jdk1.3.1/include/linux -fPIC -c Hello.c
生成 Hello.o
gcc -shared -Wl,-soname,libhello.so -o libhello.so Hello.o
生成 libhello.so(這就是linux下動態連接庫的文件名格式,就像windows下是.dll文件後綴名相似)
最後通知動態連接程序此共享文件的路徑。
export LD_LIBRARY_PATH=`pwd`:$LD_LIBRARY_PATH
編寫一個簡單的 Java 程序來測試咱們的本地方法
將以下源碼存爲 A.java:
import Hello; import java.util.*; public class A { public static void main(String argv[]) { A a = new A(); } public A() { Hello h = new Hello(); // 調用本地方法 h.SayHello("Hello world"); } }
用 javac 編譯A.java,生成A.class 向執行普通 Java 程序同樣使用 java A,咱們會看到在屏幕上出現 Hello world。 ok,完成!