Java的出現給大家開發帶來的極大的方便
但是
如果我們有大量原有的經過廣泛測試的非Java代碼
將它們全部用Java來重寫
恐怕會帶來巨大的工作量和長期的測試
如果我們的應用中需要訪問到特定的設備
甚至是僅符合公司內部信息交互規范的設備
或某個特定的操作系統才有的特性
Java就顯得有些力不從心了
面對這些問題
Sun公司在JDK
中就定義了JNI規范
它規定了Java應用程序對本地方法的調用規則
實現步驟及相關函數使用 本文將一步步說明在Linux平台下如何實現本地共享庫與Java協同工作
Hello World程序是目前標准的入門第一步
那麼
我也以類似的應用最為樣例
第一步
定義一個 Java 類
Hello
它提供SayHello方法
此時應注意兩點
為要使用的每個本地方法編寫本地方法聲明
其聲明方式與普通Java方法接口沒什麼不同
只是必須指定 native 關鍵字
如下所示
public native void SayHello(String strName);
在這個函數中
我們將根據傳進的人名
向某人問好
必須顯式地加載本地代碼庫
我們需在類的一個靜態塊中加載這個庫
static
{
System
loadLibrary(
hello
);
}
再加上必要的異常處理就生成如下源文件Hello
java
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);
}
編譯後生成Hello
class文件
第二步
生成本地鏈接庫
具體過程如下
要為以上定義的類生成 Java 本地接口頭文件
需使用javah
Java 編譯器的 javah 功能將根據Hello類生成必要的聲明
此命令將生成Hello
h文件
我們在共享庫的代碼中要包含它
javah不使默認內部命令
需要指明路徑
它在JDK的bin目錄下
在我的Linux環境下命令如下
/home/jbuilder/jdk
/bin/javah Hello
生成的Hello
h 文件 內容如下所示
/* DO NOT EDIT THIS FILE
it is machine generated */
#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
在與Helloh相同的路徑下創建一個CPP文件Hellocpp內容如下
#include Helloh
#include
//與Helloh中函數聲明相同
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\nstr);
//通知虛擬機本地代碼不再需要通過str訪問Java字符串
env>ReleaseStringUTFChars
( instring (const char *)str );
return;
}
所有的JNI調用都使用了JNIEnv*類型的指針習慣上在CPP文件中將這個變量定義為evn它是任意一個本地方法的第一個參數env指針指向一個函數指針表在VC中可以直接用>操作符訪問其中的函數
jobject 指向在此 Java 代碼中實例化的 Java 對象 LocalFunction的一個句柄相當於this指針後續的參數就是本地調用中有Java程序傳進的參數本例中只有一個String型參數對於字符串型參數因為在本地代碼中不能直接讀取Java字符串而必須將其轉換為C/C++字符串或Unicode以下是三個我們經常會用到的字符串類型處理的函數
educitycn/img_///gif >
educitycn/img_///gif >
參數array 數組對象
isCopy 如果進行拷貝指向以JNI_TRUE填充的jboolean否則指向以JNI_FALSE填充的jboolean
例如 jboolean * GetBooleanArrayElements(jbooleanArray array jboolean *isCopy)
void ReleaseXxxArrayElements(xxxArray arrayxxx *elems jint mode)
通知虛擬機不再需要從GetXxxArrayElements得到的指針
參數array 數組對象
elems 不再需要的指向數組元素的指針
mode =在更新數組元素後釋放elems緩沖器
JNI_COMMIT=在更新數組元素後不釋放elems緩沖器
JNI_ABORT=不更新數組元素釋放elems緩沖器
例如void ReleaseBooleanArrayElements(jbooleanArray arrayjboolean *elems jint mode)
xxxArray NewXxxArray(jsize len)
產生一個新的數組通常在反值類型為數組型時用到
參數len 數組中元素的個數
例如jbooleanArray NewBooleanArray(jsize len)
編譯生成共享庫
使用GCC時必須通知編譯器在何處查找此Java本地方法的支持文件並且顯式通知編譯器生成位置無關的代碼在我的環境中按如下過程編譯
gccI/home/jbuilder/jdk/includeI/home
/jbuilder/jdk/include/linux fPIC c Helloc
生成Helloo
gcc shared Wlsonamelibhelloso
o libhelloso Helloo
生成libhelloso
接下來將生成的共享庫拷貝為標准文件名
cp libhelloso libhelloso
最後通知動態鏈接程序此共享文件的路徑
export LD_LIBRARY_PATH=`pwd`
:$LD_LIBRARY_PATH
編寫一個簡單的Java程序來測試我們的本地方法
將如下源碼存為ToSayjava
import Hello;
import javautil*;
public class ToSay
{
public static void main(String argv[])
{
ToSay say = new ToSay();
}
public ToSay()
{
Hello h = new Hello();
//調用本地方法向John問好
hSayHello(John);
}
}
用javac編譯ToSayjava生成ToSayclass向執行普通Java程序一樣使用java ToSay我們會看到在屏幕上出現HelloJohn到這裡我們就將整個的本地調用編寫過程大概浏覽了一遍
應用中注意事項
.如果可以通過TCP/IP實現Java代碼與本地C/C++代碼的交互工作那麼最好不使用以上提到的JNI的方式因為一次JNI調用非常耗時大概要花~個毫秒
.在一個Applet應用中不要使用JNI因為在 applet 中可能引發安全異常
.將所有本地方法都封裝在單個類中這個類調用單個 DLL對於每種目標操作系統都可以用特定於適當平台的版本替換這個 DLL這樣就可以將本地代碼的影響減至最小並有助於將以後所需的移植問題包含在內
.本地方法要簡單盡量將生成的DLL 對任何第三方運行時 DLL 的依賴減到最小使本地方法盡量獨立以將加載DLL 和應用程序所需的開銷減到最小如果必須要運行時 DLL則應隨應用程序一起提供它們
.本地代碼運行時沒有有效地防數組越界錯誤錯誤指針引用帶來的間接錯誤等所以必須保證保證本地代碼的穩定性因為絲毫的錯誤都可能導致Java虛擬機崩潰
結束語
JNI調用規范給我們復用原有其它語言的代碼提供了簡單易用的接口可以節省大量的財力使我們可以在享受Java帶來的開發速度的同時不必放棄舊有資源
const char* GetStringUTFChars
(jstring stringjboolean* isCopy)
返回指向字符串UTF編碼的指針如果不能創建這個字符數組返回null這個指針在調用ReleaseStringUTFChar()函數之前一直有效
參數string Java字符串對象
isCopy 如果進行拷貝指向以JNI_TRUE填充的jboolean否則指向以JNI_FALSE填充的jboolean
void ReleaseStringUTFChars(jstring str const char* chars)
通知虛擬機本地代碼不再需要通過chars訪問Java字符串
參數string Java字符串對象
chars 由GetStringChars返回的指針
jstring NewStringUTF(const char *utf)
返回一個新的Java字符串並將utf內容拷貝入新串如果不能創建字符串對象
返回null通常在反值類型為string型時用到
參數utf UTF編碼的字符
From:http://tw.wingwit.com/Article/program/Java/JSP/201311/19137.html