JAVA中native方法調用


Java中native是關鍵字。它一般在本地聲明,異地用C和C++來實現。它的聲明有幾點要注意:1)native與訪問控制符前后的關系不受限制。2)必須在返回類型之前。3)它一般為非抽象類方法。4)native方法在異地實現,象抽象方法一樣,所以沒有方法體,以分號結束。如下列5種聲明:

         native public void f();                         正確。

         private native void f();                        正確。

         protected int native f();                      錯誤,返回類型位置不對,返回類型必須在native之后。

         public abstract native void f();            native必然不是abstract的。

         native int f(){}                                     錯誤,因為存在方法體{}

         public static native f();                         正確。static與native方法位置隨意。

       下例是比較典型的native的使用方式,用loadLibrary導入存在源文件目錄下的*.dll文件,然后定義native方法時,與c/c++實現的函數有相同的參數和返回類型。

 

 

  1. public class OpticalFlowCalculateDll{ 
  2.  
  3.     static{ 
  4.  
  5.         System.loadLibrary("OpticalFlow"); 
  6.  
  7.     } 
  8.  
  9.      
  10.  
  11.     public native static void setImage();     
  12.  
  13.     public native static int[] getGoodFeatureListForTrack();     
  14.  
  15.     public native static int[] getMatchedFeatureList();     
  16.  
  17.     public native static int getFeatureListSize();     
  18.  
  19.     public native static void dispose(); 
  20.  
  21. }    
[java]  view plain  copy
 
  1. public class OpticalFlowCalculateDll{  
  2.   
  3.     static{  
  4.   
  5.         System.loadLibrary("OpticalFlow");  
  6.   
  7.     }  
  8.   
  9.       
  10.   
  11.     public native static void setImage();      
  12.   
  13.     public native static int[] getGoodFeatureListForTrack();      
  14.   
  15.     public native static int[] getMatchedFeatureList();      
  16.   
  17.     public native static int getFeatureListSize();      
  18.   
  19.     public native static void dispose();  
  20.   
  21. }     

 

 

 

 

再展開來說JNI,JNI是Java Native Interface的縮寫,中文為JAVA本地調用。從Java 1.1開始,Java Native Interface (JNI)標准成為java平台的一部分,它允許Java代碼和其他語言寫的代碼進行交互。JNI一開始是為了本地已編譯語言,尤其是C和C++而設計的,但是它並不妨礙你使用其他語言,只要調用約定受支持就可以了。JNI是JDK的一部分,用於為Java提供一個本地代碼的接口。通過使用JNI編寫的程序能夠確保你的代碼能夠完全的移植到所有的平台。JNI使得運行在JVM虛擬機上的Java代碼能夠操作使用其它語言編寫的應用程序和庫,比如 C/C++以及匯編語言等。此外JNI提供的某些API還允許你把JVM嵌入到本地應用程序中。

  

  使用java與本地已編譯的代碼交互,通常會喪失平台可移植性。但是,有些情況下這樣做是可以接受的,甚至是必須的,比如,使用一些舊的庫,與硬件、操作系統進行交互,或者為了提高程序的性能。JNI標准至少保證本地代碼能工作在任何Java 虛擬機實現下。

JNI的設計目的:

  ·標准的java類庫可能不支持你的程序所需的特性。

  ·或許你已經有了一個用其他語言寫成的庫或程序,而你希望在java程序中使用它。

  ·你可能需要用底層語言實現一個小型的時間敏感代碼,比如匯編,然后在你的java程序中調用這些功能。

       本文將通過一個實例來闡述使用VC++6.0來實現JNI的完整過程。使用JNI來整合本地代碼和Java代碼的步驟是確定的,沒有再創作的余地,所以讀者可以通過本文的步驟來逐步認識到,其實Java也是"沒有什么不可以"的。

一、JNI的實現任務描述:在Java中調用windows下的消息框函數,並且從Java中傳遞一個字符串作為MessageBox函數的顯示文本參數,顯示在消息框的中間。下面讓我們一起進入這一奇妙的旅程。

Step 1:寫一個Java類,在這個類中包含了需要調用的本地方法的描述。

 

 

 

  1. //WinMsgBox.java  
  2.  
  3. package edu.netcom.jni;  
  4.  
  5. public class WinMsgBox  
  6.  
  7. {  
  8.  
  9. static{  
  10.  
  11. System.loadLibrary("WinMsgDll");    // (1)  
  12.  
  13. }  
  14.  
  15. public native void showMsgBox(String str); // (2)  
  16.  
[java]  view plain  copy
 
  1. //WinMsgBox.java   
  2.   
  3. package edu.netcom.jni;   
  4.   
  5. public class WinMsgBox   
  6.   
  7. {   
  8.   
  9. static{   
  10.   
  11. System.loadLibrary("WinMsgDll");    // (1)   
  12.   
  13. }   
  14.   
  15. public native void showMsgBox(String str); // (2)   
  16.   
  17. }  

 

 

 

 

(1)中WinMsgDll是動態鏈接文件的文件名,不用加擴展名,因為在不同的平台下動態鏈接文件擴展名是不同的,由JVM自動識別,比如在 Solaris下,會被轉換為WinMsgDll.so;而Win32環境下會轉換為WinMsgDll.dll。這個文件名必須和Step 4中生成的文件名一致。這個文件的存放位置也很重要,它只能被放在JVM屬性值java.library.path中指定的文件夾中。這個屬性值可以使用 System.getProperty("java.library.path");來查看。一般情況下,至少放在這幾個位置是確定可靠的,windows安裝目錄下的system32下面,JDK安裝目錄下的bin下面,以及調用主類文件的當前目錄。

(2)中指明了你必須用本地代碼實現的方法。

Step 2:提示符下使用命令javac -d . WinMsgBox.java編譯Step 1編寫的java文件。

此時會在當前目錄下建立一個edu/netcom/jni目錄結構,並且一個WinMsgBox.class文件存在其中。

Step 3:提示符下使用命令javah -jni edu.netcom.jni.WinMsgBox,此時會在當前目錄下產生一個edu_netcom_jni_WinMsgBox.h文件,注意這個文件名是由(包名+類名)組成,中間用(_)隔開。此文件內容如下:

 

 

  1. /* DO NOT EDIT THIS FILE - it is machine generated */  
  2.  
  3. #include <jni.h>                       // (1)  
  4.  
  5. /* Header for class edu_netcom_jni_WinMsgBox */ 
  6.  
  7. #ifndef _Included_edu_netcom_jni_WinMsgBox  
  8.  
  9. #define _Included_edu_netcom_jni_WinMsgBox  
  10.  
  11. #ifdef __cplusplus  
  12.  
  13. extern "C" {  
  14.  
  15. #endif  
  16.  
  17. /*
  18. * Class:     edu_netcom_jni_WinMsgBox
  19. * Method:    showMsgBox
  20. * Signature: (Ljava/lang/String;)V       // (2)
  21. */  
  22.  
  23. JNIEXPORT void JNICALL Java_edu_netcom_jni_WinMsgBox_showMsgBox  
  24.  
  25. (JNIEnv *, jobject, jstring);           // (3) 
  26.  
  27. #ifdef __cplusplus  
  28.  
  29. }  
  30.  
  31. #endif  
  32.  
  33. #endif 
[cpp]  view plain  copy
 
  1. /* DO NOT EDIT THIS FILE - it is machine generated */   
  2.   
  3. #include <jni.h>                       // (1)   
  4.   
  5. /* Header for class edu_netcom_jni_WinMsgBox */  
  6.   
  7. #ifndef _Included_edu_netcom_jni_WinMsgBox   
  8.   
  9. #define _Included_edu_netcom_jni_WinMsgBox   
  10.   
  11. #ifdef __cplusplus   
  12.   
  13. extern "C" {   
  14.   
  15. #endif   
  16.   
  17. /*  
  18.  
  19. * Class:     edu_netcom_jni_WinMsgBox  
  20.  
  21. * Method:    showMsgBox  
  22.  
  23. * Signature: (Ljava/lang/String;)V       // (2)  
  24.  
  25. */   
  26.   
  27. JNIEXPORT void JNICALL Java_edu_netcom_jni_WinMsgBox_showMsgBox   
  28.   
  29. (JNIEnv *, jobject, jstring);           // (3)  
  30.   
  31. #ifdef __cplusplus   
  32.   
  33. }   
  34.   
  35. #endif   
  36.   
  37. #endif  

 

 

 

 

(1)包含的jni.h存在於JDK安裝目錄下的include下面。

(2)(Ljava/lang/String;)V這是函數的標記符,當從本地方法端訪問Java端的方法時,會用到這個標記符。JNI中為每種數據類型也定義了標記符,標記符的規則請查看JNI標准文檔。

(3)在WinMsgBox.java中本地方法void showMsgBox(String str);的定義,被映射為JNIEXPORT void JNICALL Java_edu_netcom_jni_WinMsgBox_showMsgBox(JNIEnv *, jobject, jstring); 其中函數名的映射規則是(Java_包名_類名_方法名),如果存在重載的方法,則在后面還會增加每個參數的標記符。每一個方法映射到本地C函數后都會增加兩個參數:JNIEnv *和jobject,關於這兩個參數的用法將在后面闡述。另外,所有Java中的數據類型都會按一定規則進行映射為本地數據類型,這些數據類型都是在 jni.h中定義的。下面分別按照基本數據類型,和對象類型列出。

Step 4:使用VC來編寫本地方法的實現函數,最后編譯成.dll文件。過程如下:

1) 選擇new->projects(選擇Win32 Dynamic-Link Library,以Step 1中指定的庫名WinMsgDll作為工程名)->OK->An ampty DLL project->Finish。

2) 選擇Tools->Options->Directories(添加目錄D:/J2SDK1.4.2_03/INCLUDE和D: /J2SDK1.4.2_03/INCLUDE/WIN32)。在這些目錄中包含JNI所需的頭文件。

3) 將Step 3生成的edu_netcom_jni_WinMsgBox.h拷貝到WinMsgDll工程文件夾中。然后FileView中添加這個頭文件。

4) 添加源文件WinMsgDll.cpp,內容如下:

 

 

  1. include "windows.h"  
  2.  
  3. #include "edu_netcom_jni_WinMsgBox.h"  
  4.  
  5. /*
  6. * Class:     edu_netcom_jni_WinMsgBox
  7. * Method:    showMsgBox
  8. * Signature: (Ljava/lang/String;)V
  9. */  
  10.  
  11. JNIEXPORT void JNICALL Java_edu_netcom_jni_WinMsgBox_showMsgBox  
  12.  
  13. (JNIEnv * env, jobject obj, jstring str){  
  14.  
  15. const char *msg;  
  16.  
  17. msg = env->GetStringUTFChars(str,0);  
  18.  
  19. MessageBox(NULL,msg,"Java invoke",MB_OK);  
  20.  
  21. env->ReleaseStringUTFChars(str,msg);  
  22.  
[cpp]  view plain  copy
 
  1. include "windows.h"   
  2.   
  3. #include "edu_netcom_jni_WinMsgBox.h"   
  4.   
  5. /*  
  6.  
  7. * Class:     edu_netcom_jni_WinMsgBox  
  8.  
  9. * Method:    showMsgBox  
  10.  
  11. * Signature: (Ljava/lang/String;)V  
  12.  
  13. */   
  14.   
  15. JNIEXPORT void JNICALL Java_edu_netcom_jni_WinMsgBox_showMsgBox   
  16.   
  17. (JNIEnv * env, jobject obj, jstring str){   
  18.   
  19. const char *msg;   
  20.   
  21. msg = env->GetStringUTFChars(str,0);   
  22.   
  23. MessageBox(NULL,msg,"Java invoke",MB_OK);   
  24.   
  25. env->ReleaseStringUTFChars(str,msg);   
  26.   
  27. }  

 

 

 

 

5) 編譯生成WinMsgBox.dll文件。並將這個.dll文件拷貝到Step 1中說明的目錄中。

注意:

1) 我們知道dll文件有兩種指明導出函數的方法,一種是在.def文件中定義,另一種是在定義函數時使用關鍵字 __declspec(dllexport)。而在JNI中函數定義中的關鍵字JNIEXPORT實際在jni_md.h中如下定義,#define JNIEXPORT __declspec(dllexport),可見JNI默認的導出函數使用第二種。使用第二種方式產生的導出函數名會根據編譯器發生變化,在有的情況下會發生找不到導出函數的問題(我們在JSP中使用JNI時就發生了這種問題,百思不得其解,后來強行加入一個.def文件就解決了)。因此最好是使用第一種方法自己定義一個.def文件來指明導出函數,這種情況下會強制使用第一種方式產生導出函數。本例中可以加入一個WinMsgDll.def文件,內容如下:

LIBRARY      "WinMsgDll"

DESCRIPTION 'message Windows Dynamic Link Library'

EXPORTS

    ; Explicit exports can Go here

Java_edu_netcom_jni_WinMsgBox_showMsgBox

2) 從本例中,我們可以看到WinMsgBox.java決定了edu_netcom_jni_WinMsgBox.h,而后者又決定了 WinMsgDll.dll,也就是說,這是一個"牽一發而動全身"的過程,如果你改動了WinMsgBox.java,就一定要把整個步驟都走一遍(這一點一定要切記,因為這也是我們跌得鼻青臉腫后才得出的警世良言)。

3) 生成的.dll文件一定要正確拷貝到Step 1說明的目錄中,本例中是將生成的WinMsgDll.dll和Step 5中的測試文件放在同一個目錄下的(這也是我們困惑了很久才解決的問題)。

Step 5:編寫一個測試文件來測試對WinMsgDll.dll的調用。測試文件TestJNI.java內容如下:

 

 

  1. //TestJNI.java  
  2.  
  3. import edu.netcom.jni.WinMsgBox;  
  4.  
  5. public class TestJNI  
  6.  
  7. {  
  8.  
  9. public static void main(String[] args){  
  10.  
  11.      WinMsgBox box = new WinMsgBox();  
  12.  
  13.      box.showMsgBox("Wonderful!!");  
  14.  
  15.    }  
  16.  
  17. }  
[java]  view plain  copy
 
  1. //TestJNI.java   
  2.   
  3. import edu.netcom.jni.WinMsgBox;   
  4.   
  5. public class TestJNI   
  6.   
  7. {   
  8.   
  9. public static void main(String[] args){   
  10.   
  11.      WinMsgBox box = new WinMsgBox();   
  12.   
  13.      box.showMsgBox("Wonderful!!");   
  14.   
  15.    }   
  16.   
  17. }   

 

 

 

 

編譯,運行,windows下的對話框躍然屏幕中間。


免責聲明!

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



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