這篇文章主要介紹了詳解Java 本機介面JNI 使用方法的相關資料,希望透過本文大家能徹底使用JNI編程,需要的朋友可以參考下
詳解Java 本機介面JNI使用方法
對於Java程式設計師來說,Java語言的好處和優點,我想不用我說了,大家自然會說出很多一套套的。但雖然我們身為java程式設計師,但我們不得不承認java語言也有一些它本身的缺點。例如在性能、和底層打交道方面都有它的缺點。所以java就提供了一些本地接口,他主要的作用就是提供一個標準的方式讓java程式透過虛擬機與原生程式碼進行交互,這也就是我們平常說的java本地接口(JNI——java native Interface) 。它使得在 Java 虛擬機器 (VM) 內部運行的 Java 程式碼能夠與用其它程式語言(如 C、C++ 和彙編語言)編寫的應用程式和程式庫進行互通。 JNI 最重要的好處是它沒有對底層 Java 虛擬機器的實作施加任何限制。因此,Java虛擬機廠商可以在不影響虛擬機器其它部分的情況下添加對 JNI 的支援。程式設計師只需編寫一個版本的本機應用程式或函式庫,就能夠與所有支援 JNI 的 Java 虛擬機協同工作。讓我們來看看為什麼要與原生程式碼互動:
一:提高應用程式效能。 我們知道java對於c/c++、組譯語言來說,顯得比較「高階」。其實這裡的高級就是簡化了程式設計師的工作。很多底層的東西都讓java虛擬機器做了。但畢竟相對於直接存取底層來講,java多了一步虛擬機器的過程,所以在效能上比著這些原生語言稍微有點慢。
二:實作一些與底層相關的功能。 Java平台提供的標準類別函式庫,還有強大的API,雖然能完成大部分功能。但有些和底層硬體打交道的功能在java API提供的類別庫中還是無法完成。
三:與現有的使用原生程式碼編寫的程式進行整合。 在於作業系統上由c或c++等原生語言寫的軟體進行集 0成的時候,可以用JNI。
JNI 介面函數和指標
平台相關程式碼是透過呼叫 JNI 函數來存取 Java 虛擬機功能的。 JNI 函數可透過介面指標來取得。介面指標是指標的指針,它指向一個指標數組,而指標數組中的每個元素又指向一個介面函數。每個介面函數都處於陣列的某個預定偏移量中。下圖說明了介面指標的組織結構。
JNI 介面的組織與「使用介面表而不使用硬性編入的函數表的好處是使 JNI 名字空間與平台相關程式碼分開。虛擬機器可以輕鬆地提供多個版本的 JNI 函數表。例如,虛擬機器可支援以下兩個JNI 函數表:
· 一個表對非法參數進行全面檢查,適用於偵錯程式;
本地方法將JNI 介面指標當作參數來接受。 虛擬機器在從相同的 Java 執行緒中對本機方法進行多次呼叫時,保證傳遞給該本機方法的介面指標是相同的。但是,一個本機方法可被不同的 Java 執行緒所調用,因此可以接受不同的 JNI 介面指標。
#在該類別中,用System. 1oadLibrary()方法載入所需的動態連結庫。關鍵程式碼如下:
//Compute.java public class Compute{ public native double sqrt(double params); static{ //调用动态链接库 System.loadLibrary(“compute”); }
在這個過程中,由於採用了native關鍵字聲明,Java編譯器會忽略沒有代碼體的JNI方法部分。
(3)產生相關JNI方法的頭檔
这个过程的实现一般是通过利用jlavah-jni * class生成的(-jni可以省略),也可以手工生成该文件;但是由于 Java 虚拟机是根据一定的命名规范完成对JNI方法的调用,所以手工编写头文件需要特别小心。
上述文件产生的头文件部分代码如下:
//Compute.h extern“C”{ JNIEXPORT jdoubleJNICALL Java_Compute_comp(JNI-Env *, jobject, jdoubleArray);
JNI函数名称分为三部分:首先是Java关键字,供Java虚拟机识别;然后是调用者类名称(全限定的类名,其中用下划线代替名称分隔符);最后是对应的方法名称,各段名称之间用下划线分割。
JNI函数的参数也由三部分组成:首先是JNIEnv *,是一个指向JNI运行环境的指针;第二个参数随本地方法是静态还是非静态而有所不同一一非静态本地方法的第二个参数是对对象的引用,而静态本地方法的第二个参数是对其Java类的引用;其余的参数对应通常Java方法的参数,参数类型需要根据一定规则进行映射。
(4)编写相应方法的实现代码
在编码过程中,需要注意变量的长度问题,例如Java的整型变量长度为32位,而C语言为16位,所以要仔细核对变量类型映射表,防止在传值过程中出现问题。
(5)将JNI实现代码编译成动态链接库
编译过程是利用C/C++编译器实现的,在windows平台上,编译和连接的结果是动态链接库DLL文件。当要使用生成的动态链接库时,调用者类中需要显式调用该链接库dll文件。
经过上述处理,基本上完成了一个包含本地化方法的Java类的开发。
附录:将Jav类型映射到本地 C 类型
基本类型和本地等效类型 | ||
Java 类型 |
本地类型 |
说明 |
boolean |
jboolean |
无符号,8 位 |
byte |
jbyte |
无符号,8 位 |
char |
jchar |
无符号,16 位 |
short |
jshort |
有符号,16 位 |
int |
jint |
有符号,32 位 |
long |
jlong |
有符号,64 位 |
float |
jfloat |
32 位 |
double |
jdouble |
64 位 |
void |
void |
N/A |
为了使用方便,特提供以下定义。
#define JNI_FALSE 0 #define JNI_TRUE 1
jsize 整数类型用于描述主要指数和大小:
typedef jint jsize;
故障排除
当使用 JNI 从 Java 程序访问本机代码时,您会遇到许多问题。您会遇到的三个最常见的错误是:
1)无法找到动态链接。它所产生的错误消息是:java.lang.UnsatisfiedLinkError。这通常指无法找到共享库,或者无法找到共享库内特定的本机方法。
2)无法找到共享库文件。当用 System.loadLibrary(String libname) 方法(参数是文件名)装入库文件时,请确保文件名拼写正确以及没有指定扩展名。还有,确保库文件的位置在类路径中,从而确保 JVM 可以访问该库文件。
3)无法找到具有指定说明的方法。确保您的 C/C++ 函数实现拥有与头文件中的函数说明相同的说明。
结束语
从 Java 调用 C 或 C++ 本机代码(虽然不简单)是 Java 平台中一种良好集成的功能。虽然 JNI 支持 C 和 C++,但 C++ 接口更清晰一些并且通常比 C 接口更可取。正如您已经看到的,调用 C 或 C++ 本机代码需要赋予函数特殊的名称,并创建共享库文件。当利用现有代码库时,更改代码通常是不可取的。要避免这一点,在 C++ 中,通常创建代理代码或代理类,它们有专门的 JNI 所需的命名函数。然后,这些函数可以调用底层库函数,这些库函数的说明和实现保持不变。
以上是Java本地介面JNI所使用的方法詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!