在Linux下如何使用JNI

你們都知道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,完成!

相關文章
相關標籤/搜索