Java jvm級別native關鍵詞、JNI詳解


1.native關鍵詞的引入

  再完美的編程語言也有自己的不足之處,當然Java也不例外,Java的不足之處除了體現在運行速度(這點往往被一些其他編程語言使用者所詬病)上要比傳統的C++慢許多之外,Java無法直接訪問到操作系統底層(如系統硬件等),為此Java使用native(原生的)關鍵詞來擴展Java程序的功能。native關鍵字說明其修飾的方法是一個原生態方法,方法對應的實現不是在當前文件,而是在用其他語言(如C和C++)實現的文件中。Java語言本身不能對操作系統底層進行訪問和操作,但是可以通過JNI接口調用其他語言來實現對底層的訪問。JNI是Java Native Interface的縮寫,它提供了若干的API實現了Java和其他語言的通信(主要C&C++)。從Java1.1開始,JNI標准成為java平台的一部分,它允許Java代碼和其他語言寫的代碼進行交互。JNI一開始是為了本地已編譯語言,尤其是C和C++而設計的,但是它並不妨礙你使用其他編程語言,只要調用約定受支持就可以了。使用java與本地已編譯的代碼交互,通常會喪失平台可移植性。但是,有些情況下這樣做是可以接受的,甚至是必須的。例如,使用一些舊的庫,與硬件、操作系統進行交互,或者為了提高程序的性能。JNI標准至少要保證本地代碼能工作在任何Java 虛擬環境。簡而言之,JNI功能即為:Java和本地代碼間的雙向交互。JNI允許Java代碼使用以其他語言編寫的代碼和代碼庫。Invocation API(JNI的一部分)可以用來將Java虛擬機(JVM)嵌入到本機應用程序中,從而允許程序員從本機代碼內部調用Java代碼。

2.JNI的使用場合

1、JAVA程序和本地程序使用TCP/IP或者IPC進行交互。
2、當用JAVA程序連接本地數據庫時,使用JDBC提供的API。
3、JAVA程序可以使用分布式對象技術,如JAVA IDL API。
這些方案的共同點是,JAVA和C處於不同的線程,或者不同的機器上。這樣,當本地程序崩潰時,不會影響到JAVA程序。
下面這些場合中,同一進程內JNI的使用無法避免:
1、程序當中用到了JAVA API不提供的特殊系統環境才會有的特征。而跨進程操作又不現實。
2、可能想訪問一些己有的本地庫,但又不想付出跨進程調用時的代價,如效率,內存,數據傳遞方面。
3、JAVA程序當中的一部分代碼對效率要求非常高,如算法計算,圖形渲染等。
總之,只有當必須在同一進程中調用本地代碼時,再使用JNI。

3.JNI的書寫步驟

第一步:編寫帶有native聲明的方法的Java類

第二步:使用javac命令編譯編寫的Java類

第三步:使用java -jni 來生成后綴名為.h的頭文件

第四步:使用其他語言(C、C++)實現本地方法

第五步:將本地方法編寫的文件生成動態鏈接庫

第六步:運行程序

4.一個JNI的簡單應用例子(以HelloJNI為例子)

第一步:編寫帶有native聲明的方法的Java類

public class HelloJNI {
    public native void displayHelloJNI();//所有native關鍵詞修飾的都是對本地的聲明
    static {
        System.loadLibrary("hello");//載入本地庫
    }
    public static void main(String[] args) {
        new JNI().displayHelloJNI();
    }
}

聲明native方法:如果想將一個方法做為一個本地方法的話,那么就必須聲明該方法為native,並且不能實現。 Load動態庫:System.loadLibrary("hello");加載動態庫(可以這樣理解:方法 displayHelloJNI()沒有實現,但是在后續步驟中就直接使用了,所以必須在使用之前對它進行初始化),這里一般是以static塊進行加載的。同時需要注意的是System.loadLibrary()的參數“hello”是動態庫的名字。

第二步:使用javac命令編譯編寫的Java類

javac HelloJNI.java

注意:JDK環境變量的配置

第三步:使用java -jni 來生成后綴名為.h的頭文件( 生成擴展名為h的頭文件javah HelloJNI)

頭文件javah HelloJNI內容如下:

 1 /*DO NOT EDI TTHIS FILE - it is mach inegenerated*/
 2 #include<jni.h>
 3 /*Header for class HelloJNI*/
 4  
 5 #ifndef_Included_HelloJNI
 6 #define_Included_HelloJNI
 7 #ifdef__cplusplus
 8 extern"C"{
 9 #endif
10 /*
11 *Class:HelloJNI
12 *Method:displayHelloJNI
13 *Signature:()V
14 */
15 JNIEXPORTvoidJNICALL
16 Java_HelloJNI_displayHelloJNI(JNIEnv*,jobject);
17  
18 #ifdef__cplusplus
19 }
20 #endif
21 #endif

注意:這個h文件相當於在java里面的接口,這里聲明了一個Java_HelloJNI_displayHelloJNI (JNIEnv *, jobject)方法,然后在本地方法里面實現這個方法,也就是說在編寫C/C++程序的時候所使用的方法名必須和這里的一致

第四步:使用其他語言(C、C++)實現本地方法(編寫本地方法實現和由javah命令生成的頭文件里面聲明的方法名相同的方法)

 1 #include"jni.h"
 2 #include"HelloJNI.h"
 3  
 4 //#includeotherheaders
 5  
 6 JNIEXPORT void JNICALL
 7 Java_HelloJNI_displayHelloJNI(JNIEnv*env,jobject obj)
 8 {
 9 printf("HelloJNI!\n");
10 return;
11 }

注意:代碼第一行需要將jni.h(該文件可以在%JAVA_HOME%/include文件夾下面找到)文件引入,因為在程序中的JNIEnv、 jobject等類型都是在該頭文件中定義的;另外在第2行需要將HelloJNI.h頭文件引入,然后保存為 HelloJNIImpl.c。

第五步:將本地方法編寫的文件生成動態鏈接庫

這里以在Windows中為例,需要生成dll文件。在保存HelloWorldJNI.c文件夾下面,使用VC的編輯器cl為:cl -I%java_home%\include -I%java_home%\include\win32 -LD HelloJNIImp.c -Fehello.dll 注意:生成的dll文件名在選項-Fe后面配置,這里是hello,因為在HelloJNI.java文件中我們loadLibary的時候使用的名字是hello。當然這里修改之后那里也需要修改。另外需要將-I%java_home%\include -I%java_home%\include\win32參數加上,因為在第四步里面編寫本地方法的時候引入了jni.h文件。
如果配置了MinGW,也可以這樣來編譯:gcc -Wall -D_JNI_IMPLEMENTATION_ -Wl,--kill-at -Id:/java/include –Id:/java/include/win32 -shared -o (輸出的dll文件名,如sum.dll) (輸入的c/c++源文件,如abc.c)。
第六步:運行程序
運行javaHelloJNI;如果用eclipse/myeclipse,需將dll或so文件放在項目下,而不是src及其子目錄下;如果用命令行編譯,把dll文件放在該包的同目錄下。

 

 

 
 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM