JNI允許Java代碼使用以其它語言(譬如 C 和 C++)編寫的代碼和代碼庫。
Invocation API(JNI的一部分)可以用來將Java虛擬機(JVM)嵌入到本機應用程序中,從而允許程序員從本機代碼內部調用Java 代碼。
也許不少人覺得Java已經足夠強大,爲什麼要需要JNI這種東西呢?
我們知道Java是一種平臺無關性的語言,平臺對於上層的java代碼來說是透明的,所以在多數時間我們是不需要JNI的,但是假如你遇到了如下的三種情況之一呢?
1.你的Java代碼,需要得到一個文件的屬性。但是你找遍了JDK幫助文檔也找不到相關的API。
2.在本地還有一個別的系統,不過他不是Java語言實現的,這個時候你的老闆要求你把兩套系統整合到一起。
二、環境需求
JNI 最常見的兩個應用:從Java程序調用C/C++,以及從C/C++程序調用Java代碼
java.exe: Java 虛擬機(JVM):隨 SDK 一起提供的 。
javah.exe: 本機方法 C 文件生成器:隨 SDK 一起提供的 。
3、能夠創建共享庫的 C 和 C++ 編譯器。
最常見的兩個 C 編譯器是用於Windows的Visual C++和用於基於UNIX系統的 gcc/cc。
因此,後面我們將會介紹在兩種環境下的JNI編程例子。
【教程二】windows下java JNI編程技巧——JAVA調用c/c++(0)
一、使用情況
當無法用Java語言編寫整個應用程序時,JNI允許您使用本機代碼。
在下列典型情況下,您可能決定使用本機代碼:
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->
1、希望用更低級、更快的編程語言去實現對時間有嚴格要求的代碼。
2、希望從 Java 程序訪問舊代碼或代碼庫。
3、需要標準 Java 類庫中不支持的依賴於平臺的特性。
二、所需軟件
eclipse3.4.1、JDK6、VC6.0
三、步驟分析
從 Java 程序調用 C 或 C ++ 代碼的過程由六個步驟組成:
我們將在下面幾頁中深入討論每個步驟,但還是先讓我們迅速地瀏覽一下它們:
1、編寫 Java 代碼。
我們將從編寫 Java 類開始,這些類執行三個任務:
1)聲明將要調用的本機方法;
2)裝入包含本機代碼的共享庫;
3)然後調用該本機方法。
2、編譯 Java 代碼。
在使用 Java 類之前,必須成功地將它們編譯成字節碼。
3、創建C/C++頭文件。
C/C++頭文件將聲明想要調用的本機函數說明。
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->
然後,這個頭文件與 C/C++ 函數實現(請參閱步驟 4)一起來創建共享庫(請參閱步驟 5)。
4、編寫 C/C++ 代碼。
這一步實現 C 或 C++ 源代碼文件中的函數。
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->
C/C++ 源文件必須包含步驟 3 中創建的頭文件。
5、創建共享庫文件。
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->
從步驟 4 中創建的C源代碼文件來創建共享庫文件。
6、運行 Java 程序。
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->
運行該代碼,並查看它是否有用。我們還將討論一些用於解決常見錯誤的技巧。
【教程三】windows下java JNI編程技巧——JAVA調用c/c++(1)
步驟 1:編寫 Java 代碼
我們從編寫 Java 源代碼文件開始,它將聲明本機方法(或方法),裝入包含本機代碼的共享庫,然後實際調用本機方法。
這裏是名爲JNI_javaCallc_test:
直接使用文本編輯器或在ecilpos中建立工程敲入以下代碼:
[cpp] view plaincopy
<!--[if !supportLists]-->1. <!--[endif]-->package test;
<!--[if !supportLists]-->2. <!--[endif]-->
<!--[if !supportLists]-->3. <!--[endif]-->public class JNI_javaCallc_test {
<!--[if !supportLists]-->4. <!--[endif]-->
<!--[if !supportLists]-->5. <!--[endif]--> //c/c++本地方法
<!--[if !supportLists]-->6. <!--[endif]--> public native int intMethod(int n);
<!--[if !supportLists]-->7. <!--[endif]-->
<!--[if !supportLists]-->8. <!--[endif]--> public native boolean booleanMethod(boolean bool);
<!--[if !supportLists]-->9. <!--[endif]-->
<!--[if !supportLists]-->10. <!--[endif]--> public native String stringMethod(String text);
<!--[if !supportLists]-->11. <!--[endif]-->
<!--[if !supportLists]-->12. <!--[endif]--> public native int intArrayMethod(int[] intArray);
<!--[if !supportLists]-->13. <!--[endif]-->
<!--[if !supportLists]-->14. <!--[endif]--> //java main方法
<!--[if !supportLists]-->15. <!--[endif]--> public static void main(String[] args){
<!--[if !supportLists]-->16. <!--[endif]--> //包含c語言動態庫
<!--[if !supportLists]-->17. <!--[endif]--> System.loadLibrary("test_JNI_javaCallc_test");
<!--[if !supportLists]-->18. <!--[endif]-->
<!--[if !supportLists]-->19. <!--[endif]--> JNI_javaCallc_test sample = new JNI_javaCallc_test();
<!--[if !supportLists]-->20. <!--[endif]-->
<!--[if !supportLists]-->21. <!--[endif]--> int square = sample.intMethod(5);
<!--[if !supportLists]-->22. <!--[endif]-->
<!--[if !supportLists]-->23. <!--[endif]--> boolean bool = sample.booleanMethod(true);
<!--[if !supportLists]-->24. <!--[endif]-->
<!--[if !supportLists]-->25. <!--[endif]--> String text =sample.stringMethod("JAVA");
<!--[if !supportLists]-->26. <!--[endif]-->
<!--[if !supportLists]-->27. <!--[endif]--> int sum = sample.intArrayMethod(new int[] { 1, 1, 2, 3, 5, 8, 13 });
<!--[if !supportLists]-->28. <!--[endif]-->
<!--[if !supportLists]-->29. <!--[endif]--> System.out.println("intMethod: " + square);
<!--[if !supportLists]-->30. <!--[endif]-->
<!--[if !supportLists]-->31. <!--[endif]--> System.out.println("booleanMethod: " + bool);
<!--[if !supportLists]-->32. <!--[endif]-->
<!--[if !supportLists]-->33. <!--[endif]--> System.out.println("stringMethod: " + text);
<!--[if !supportLists]-->34. <!--[endif]-->
<!--[if !supportLists]-->35. <!--[endif]--> System.out.println("intArrayMethod: " + sum);
<!--[if !supportLists]-->36. <!--[endif]--> }
<!--[if !supportLists]-->37. <!--[endif]-->}
這段代碼做了些什麼?
首先,請注意對 native 關鍵字的使用,它只能隨方法一起使用。
native 關鍵字告訴 Java 編譯器:方法是用 Java 類之外的本機代碼實現的,但其聲明卻在Java 中。只能在 Java 類中聲明本機方法,而不能實現它(但是不能聲明爲抽象的方法,使用native關鍵字即可),所以本機方法不能擁有方法主體。
現在,讓我們逐行研究一下代碼:
從第 6 行到第 12 行,我們聲明瞭四個 native 方法。
在第 17 行,我們裝入了包含這些本機方法的實現的共享庫文件。(到步驟 5 時,我們將創建該共享庫文件。)
最終,從第 21 行到第 27 行,我們調用了本機方法。
注:這個操作和調用非本機 Java 方法的操作沒有差異。
【教程四】windows下java JNI編程技巧——JAVA調用c/c++(2)
步驟 2:編譯 Java 代碼
接下來,我們需要將 Java 代碼編譯成字節碼。
完成這一步的方法之一是使用隨SDK一起提供的Java編譯器javac。
用來將 Java 代碼編譯成字節碼的命令是:
cd test
javac JNI_javaCallc_test.java
如果是在eclipse環境下編寫的以上代碼,文件保存時會自動在工程目錄的bin下生成以上java文件
步驟 3:創建 C/C++ 頭文件
第三步是創建 C/C++ 頭文件,它定義本機函數說明。
完成這一步的方法之一是使用 javah.exe,它是隨 SDK 一起提供的本機方法 C 存根生成器工具。
這個工具被設計成用來創建頭文件,該頭文件爲在 Java 源代碼文件中所找到的每個 native 方法定義 C 風格的函數。
這裏使用的命令是:
cd test
javah -classpath . test.JNI_javaCallc_test
注意.和test之間有空格
會生成以下文件:
[cpp] view plaincopy
<!--[if !supportLists]-->1. <!--[endif]--><span style="font-family:SimSun;font-size:16px;">/* DO NOT EDIT THIS FILE - it is machine generated */
<!--[if !supportLists]-->2. <!--[endif]-->#include <jni.h>
<!--[if !supportLists]-->3. <!--[endif]-->/* Header for class test_JNI_javaCallc_test */
<!--[if !supportLists]-->4. <!--[endif]-->
<!--[if !supportLists]-->5. <!--[endif]-->#ifndef _Included_test_JNI_javaCallc_test
<!--[if !supportLists]-->6. <!--[endif]-->#define _Included_test_JNI_javaCallc_test
<!--[if !supportLists]-->7. <!--[endif]-->#ifdef __cplusplus
<!--[if !supportLists]-->8. <!--[endif]-->extern "C" {
<!--[if !supportLists]-->9. <!--[endif]-->#endif
<!--[if !supportLists]-->10. <!--[endif]-->/*
<!--[if !supportLists]-->11. <!--[endif]--> * Class: test_JNI_javaCallc_test
<!--[if !supportLists]-->12. <!--[endif]--> * Method: intMethod
<!--[if !supportLists]-->13. <!--[endif]--> * Signature: (I)I
<!--[if !supportLists]-->14. <!--[endif]--> */
<!--[if !supportLists]-->15. <!--[endif]-->JNIEXPORT jint JNICALL Java_test_JNI_1javaCallc_1test_intMethod
<!--[if !supportLists]-->16. <!--[endif]--> (JNIEnv *, jobject, jint);
<!--[if !supportLists]-->17. <!--[endif]-->
<!--[if !supportLists]-->18. <!--[endif]-->/*
<!--[if !supportLists]-->19. <!--[endif]--> * Class: test_JNI_javaCallc_test
<!--[if !supportLists]-->20. <!--[endif]--> * Method: booleanMethod
<!--[if !supportLists]-->21. <!--[endif]--> * Signature: (Z)Z
<!--[if !supportLists]-->22. <!--[endif]--> */
<!--[if !supportLists]-->23. <!--[endif]-->JNIEXPORT jboolean JNICALL Java_test_JNI_1javaCallc_1test_booleanMethod
<!--[if !supportLists]-->24. <!--[endif]--> (JNIEnv *, jobject, jboolean);
<!--[if !supportLists]-->25. <!--[endif]-->
<!--[if !supportLists]-->26. <!--[endif]-->/*
<!--[if !supportLists]-->27. <!--[endif]--> * Class: test_JNI_javaCallc_test
<!--[if !supportLists]-->28. <!--[endif]--> * Method: stringMethod
<!--[if !supportLists]-->29. <!--[endif]--> * Signature: (Ljava/lang/String;)Ljava/lang/String;
<!--[if !supportLists]-->30. <!--[endif]--> */
<!--[if !supportLists]-->31. <!--[endif]-->JNIEXPORT jstring JNICALL Java_test_JNI_1javaCallc_1test_stringMethod
<!--[if !supportLists]-->32. <!--[endif]--> (JNIEnv *, jobject, jstring);
<!--[if !supportLists]-->33. <!--[endif]-->
<!--[if !supportLists]-->34. <!--[endif]-->/*
<!--[if !supportLists]-->35. <!--[endif]--> * Class: test_JNI_javaCallc_test
<!--[if !supportLists]-->36. <!--[endif]--> * Method: intArrayMethod
<!--[if !supportLists]-->37. <!--[endif]--> * Signature: ([I)I
<!--[if !supportLists]-->38. <!--[endif]--> */
<!--[if !supportLists]-->39. <!--[endif]-->JNIEXPORT jint JNICALL Java_test_JNI_1javaCallc_1test_intArrayMethod
<!--[if !supportLists]-->40. <!--[endif]--> (JNIEnv *, jobject, jintArray);
<!--[if !supportLists]-->41. <!--[endif]-->
<!--[if !supportLists]-->42. <!--[endif]-->#ifdef __cplusplus
<!--[if !supportLists]-->43. <!--[endif]-->}
<!--[if !supportLists]-->44. <!--[endif]-->#endif
<!--[if !supportLists]-->45. <!--[endif]-->#endif<strong>
<!--[if !supportLists]-->46. <!--[endif]--></strong></span>
關於 C/C++ 頭文件
如您可能已經注意到的那樣,JNI_javaCallc_test.h 中的 C/C++ 函數說明和JNI_javaCallc_test.java 中的 Java native 方法聲明有很大差異。
1、JNIEXPORT 和 JNICALL 是用於導出函數的、依賴於編譯器的指示符。
2、返回類型、參數類型是映射到 Java 類型的 C/C++ 類型,比如:jstring,jint
現在來介紹下JNI裏的數據類型:
在C++裏,編譯器會很據所處的平臺來爲一些基本的數據類型來分配長度,因此也就造成了平臺不一致性,而這個問題在Java中則不存在,因爲有JVM的緣故,所以Java中的基本數據類型在所有平臺下得到的都是相同的長度,比如int的寬度永遠都是32位。基於這方面的原因,java和c++的基本數據類型就需要實現一些mapping,保持一致性。
下面的表可以概括:下標列舉了常見的c/c++到到java的類型映射表。
Java類型 |
本地類型 |
JNI中定義的別名 |
int |
long |
jint |
long |
_int64 |
jlong |
byte |
signed char |
jbyte |
boolean |
unsigned char |
jboolean |
char |
unsigned short |
jchar |
short |
short |
jshort |
float |
float |
jfloat |
double |
double |
jdouble |
Object |
_jobject* |
jobject |
JNI的設計者其實已經幫我們取好了相應的別名以方便記憶。如果想了解一些更加細緻的信息,可以去看一些jni.h這個頭文件,各種數據類型的定義以及別名就被定義在這個文件中。
除了 Java 聲明中的一般參數以外,所有這些函數的參數表中都有一個指向 JNIEnv 和 jobject 的指針。
指向 JNIEnv 的指針實際上是一個指向函數指針表的指針。
正如將要在步驟4 中看到的,這些函數提供各種用來在C和C++中操作Java數據的能力。
jobject 參數引用當前對象
因此,如果C或C++代碼需要引用Java函數,則這個jobject充當引用或指針,返回調用的 Java 對象。
函數名本身是由前綴“Java_”加全限定類名,再加下劃線和方法名構成的。
【教程五】windows下java JNI編程技巧——JAVA調用c/c++(3)
步驟 4:編寫 C/C++ 代碼
當談到編寫 C/C++ 函數實現時,有一點需要牢記:說明必須和 JNI_javaCallc_test.h 的函數聲明完全一樣。
我們將研究用於 C 實現和 C++ 實現的完整代碼,然後討論兩者之間的差異。
C函數實現
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->
以下是 JNI_javaCallc_test.c,它是用 C 編寫的實現:
[cpp] view plaincopy
<!--[if !supportLists]-->1. <!--[endif]-->#include <jni.h>
<!--[if !supportLists]-->2. <!--[endif]-->/* Header for class test_JNI_javaCallc_test */
<!--[if !supportLists]-->3. <!--[endif]-->
<!--[if !supportLists]-->4. <!--[endif]-->
<!--[if !supportLists]-->5. <!--[endif]-->/*
<!--[if !supportLists]-->6. <!--[endif]--> * Class: test_JNI_javaCallc_test
<!--[if !supportLists]-->7. <!--[endif]--> * Method: intMethod
<!--[if !supportLists]-->8. <!--[endif]--> * Signature: (I)I
<!--[if !supportLists]-->9. <!--[endif]--> */
<!--[if !supportLists]-->10. <!--[endif]-->JNIEXPORT jint JNICALL Java_test_JNI_1javaCallc_1test_intMethod(JNIEnv *env, jobject obj, jint num)
<!--[if !supportLists]-->11. <!--[endif]--> {
<!--[if !supportLists]-->12. <!--[endif]--> return num * num;
<!--[if !supportLists]-->13. <!--[endif]--> }
<!--[if !supportLists]-->14. <!--[endif]-->
<!--[if !supportLists]-->15. <!--[endif]-->/*
<!--[if !supportLists]-->16. <!--[endif]--> * Class: test_JNI_javaCallc_test
<!--[if !supportLists]-->17. <!--[endif]--> * Method: booleanMethod
<!--[if !supportLists]-->18. <!--[endif]--> * Signature: (Z)Z
<!--[if !supportLists]-->19. <!--[endif]--> */
<!--[if !supportLists]-->20. <!--[endif]-->JNIEXPORT jboolean JNICALL Java_test_JNI_1javaCallc_1test_booleanMethod
<!--[if !supportLists]-->21. <!--[endif]--> (JNIEnv *env, jobject obj, jboolean boolean) {
<!--[if !supportLists]-->22. <!--[endif]--> return!boolean;
<!--[if !supportLists]-->23. <!--[endif]-->}
<!--[if !supportLists]-->24. <!--[endif]-->/*
<!--[if !supportLists]-->25. <!--[endif]--> * Class: test_JNI_javaCallc_test
<!--[if !supportLists]-->26. <!--[endif]--> * Method: stringMethod
<!--[if !supportLists]-->27. <!--[endif]--> * Signature: (Ljava/lang/String;)Ljava/lang/String;
<!--[if !supportLists]-->28. <!--[endif]--> */
<!--[if !supportLists]-->29. <!--[endif]-->JNIEXPORT jstring JNICALL Java_test_JNI_1javaCallc_1test_stringMethod
<!--[if !supportLists]-->30. <!--[endif]--> (JNIEnv *env, jobject obj, jstring string)
<!--[if !supportLists]-->31. <!--[endif]--> {
<!--[if !supportLists]-->32. <!--[endif]--> const char *str = (*env)->GetStringUTFChars(env, string, 0);
<!--[if !supportLists]-->33. <!--[endif]--> char cap[128];
<!--[if !supportLists]-->34. <!--[endif]--> strcpy(cap, str);
<!--[if !supportLists]-->35. <!--[endif]--> (*env)->ReleaseStringUTFChars(env, string, str);
<!--[if !supportLists]-->36. <!--[endif]--> return (*env)->NewStringUTF(env, strupr(cap));
<!--[if !supportLists]-->37. <!--[endif]-->}
<!--[if !supportLists]-->38. <!--[endif]-->
<!--[if !supportLists]-->39. <!--[endif]-->/*
<!--[if !supportLists]-->40. <!--[endif]--> * Class: test_JNI_javaCallc_test
<!--[if !supportLists]-->41. <!--[endif]--> * Method: intArrayMethod
<!--[if !supportLists]-->42. <!--[endif]--> * Signature: ([I)I
<!--[if !supportLists]-->43. <!--[endif]--> */
<!--[if !supportLists]-->44. <!--[endif]-->JNIEXPORT jint JNICALL Java_test_JNI_1javaCallc_1test_intArrayMethod
<!--[if !supportLists]-->45. <!--[endif]--> (JNIEnv *env, jobject obj, jintArray array)
<!--[if !supportLists]-->46. <!--[endif]--> {
<!--[if !supportLists]-->47. <!--[endif]--> int i, sum = 0;
<!--[if !supportLists]-->48. <!--[endif]--> jsize len = (*env)->GetArrayLength(env, array);
<!--[if !supportLists]-->49. <!--[endif]--> jint*body = (*env)->GetIntArrayElements(env, array, 0);
<!--[if !supportLists]-->50. <!--[endif]--> for(i=0; i<len; i++)
<!--[if !supportLists]-->51. <!--[endif]--> { sum += body[i];
<!--[if !supportLists]-->52. <!--[endif]--> }
<!--[if !supportLists]-->53. <!--[endif]--> (*env)->ReleaseIntArrayElements(env, array, body, 0);
<!--[if !supportLists]-->54. <!--[endif]--> return sum;
<!--[if !supportLists]-->55. <!--[endif]-->}
C++ 函數實現
[cpp] view plaincopy
<!--[if !supportLists]-->1. <!--[endif]-->#include <jni.h>
<!--[if !supportLists]-->2. <!--[endif]-->/* Header for class test_JNI_javaCallc_test */
<!--[if !supportLists]-->3. <!--[endif]-->
<!--[if !supportLists]-->4. <!--[endif]-->
<!--[if !supportLists]-->5. <!--[endif]-->/*
<!--[if !supportLists]-->6. <!--[endif]--> * Class: test_JNI_javaCallc_test
<!--[if !supportLists]-->7. <!--[endif]--> * Method: intMethod
<!--[if !supportLists]-->8. <!--[endif]--> * Signature: (I)I
<!--[if !supportLists]-->9. <!--[endif]--> */
<!--[if !supportLists]-->10. <!--[endif]-->JNIEXPORT jint JNICALL Java_test_JNI_1javaCallc_1test_intMethod(JNIEnv *env, jobject obj, jint num)
<!--[if !supportLists]-->11. <!--[endif]--> {
<!--[if !supportLists]-->12. <!--[endif]--> return num * num;
<!--[if !supportLists]-->13. <!--[endif]--> }
<!--[if !supportLists]-->14. <!--[endif]-->
<!--[if !supportLists]-->15. <!--[endif]-->/*
<!--[if !supportLists]-->16. <!--[endif]--> * Class: test_JNI_javaCallc_test
<!--[if !supportLists]-->17. <!--[endif]--> * Method: booleanMethod
<!--[if !supportLists]-->18. <!--[endif]--> * Signature: (Z)Z
<!--[if !supportLists]-->19. <!--[endif]--> */
<!--[if !supportLists]-->20. <!--[endif]-->JNIEXPORT jboolean JNICALL Java_test_JNI_1javaCallc_1test_booleanMethod
<!--[if !supportLists]-->21. <!--[endif]--> (JNIEnv *env, jobject obj, jboolean boolean) {
<!--[if !supportLists]-->22. <!--[endif]--> return!boolean;
<!--[if !supportLists]-->23. <!--[endif]-->}
<!--[if !supportLists]-->24. <!--[endif]-->/*
<!--[if !supportLists]-->25. <!--[endif]--> * Class: test_JNI_javaCallc_test
<!--[if !supportLists]-->26. <!--[endif]--> * Method: stringMethod
<!--[if !supportLists]-->27. <!--[endif]--> * Signature: (Ljava/lang/String;)Ljava/lang/String;
<!--[if !supportLists]-->28. <!--[endif]--> */
<!--[if !supportLists]-->29. <!--[endif]-->JNIEXPORT jstring JNICALL Java_test_JNI_1javaCallc_1test_stringMethod
<!--[if !supportLists]-->30. <!--[endif]--> (JNIEnv *env, jobject obj, jstring string)
<!--[if !supportLists]-->31. <!--[endif]--> {
<!--[if !supportLists]-->32. <!--[endif]--> constchar *str = env->GetStringUTFChars(string, 0);
<!--[if !supportLists]-->33. <!--[endif]--> char cap[128];
<!--[if !supportLists]-->34. <!--[endif]--> strcpy(cap, str);
<!--[if !supportLists]-->35. <!--[endif]--> env->ReleaseStringUTFChars(string, str);
<!--[if !supportLists]-->36. <!--[endif]--> returnenv->NewStringUTF(strupr(cap));
<!--[if !supportLists]-->37. <!--[endif]-->}
<!--[if !supportLists]-->38. <!--[endif]-->
<!--[if !supportLists]-->39. <!--[endif]-->/*
<!--[if !supportLists]-->40. <!--[endif]--> * Class: test_JNI_javaCallc_test
<!--[if !supportLists]-->41. <!--[endif]--> * Method: intArrayMethod
<!--[if !supportLists]-->42. <!--[endif]--> * Signature: ([I)I
<!--[if !supportLists]-->43. <!--[endif]--> */
<!--[if !supportLists]-->44. <!--[endif]-->JNIEXPORT jint JNICALL Java_test_JNI_1javaCallc_1test_intArrayMethod
<!--[if !supportLists]-->45. <!--[endif]--> (JNIEnv *env, jobject obj, jintArray array)
<!--[if !supportLists]-->46. <!--[endif]--> {
<!--[if !supportLists]-->47. <!--[endif]--> int i,sum = 0;
<!--[if !supportLists]-->48. <!--[endif]--> jsizelen = env->GetArrayLength(array);
<!--[if !supportLists]-->49. <!--[endif]--> jint*body = env->GetIntArrayElements(array, 0);
<!--[if !supportLists]-->50. <!--[endif]--> for(i=0; i<len; i++)
<!--[if !supportLists]-->51. <!--[endif]--> {
<!--[if !supportLists]-->52. <!--[endif]--> sum += body[i];
<!--[if !supportLists]-->53. <!--[endif]--> }
<!--[if !supportLists]-->54. <!--[endif]--> env->ReleaseIntArrayElements(array, body, 0);
<!--[if !supportLists]-->55. <!--[endif]--> returnsum;
<!--[if !supportLists]-->56. <!--[endif]-->}
C 和 C++ 函數實現的比較
唯一的差異在於用來訪問 JNI 函數的方法。
在C中,JNI 函數調用由“(*env)->”作前綴,目的是爲了取出函數指針所引用的值。
在 C++ 中,JNIEnv 類擁有處理函數指針查找的內聯成員函數。
下面將說明這個細微的差異,其中,這兩行代碼訪問同一函數,但每種語言都有各自的語法
C語法: jsize len = (*env)->GetArrayLength(env,array);
C++語法: jsize len =env->GetArrayLength(array);
【教程六】windows下java JNI編程技巧——JAVA調用c/c++(4)
步驟 5:創建共享庫文件
接下來,我們創建包含本機代碼的共享庫文件。
大多數 C 和 C++ 編譯器除了可以創建機器代碼可執行文件以外,也可以創建共享庫文件。
用來創建共享庫文件的命令取決於您使用的編譯器。
下面是在 Windows執行的命令。
Windows:
使用visual studio commandprompt工具cl.exe
cl -I"C:\Program Files\Java\jdk1.6.0_10\include" -I"C:\Program Files\Java\jdk1.6.0_10\include\win32" -LD test_JNI_javaCallc_test.c -Fe test_JNI_javaCallc_test.dll
也可以使用vc6.0直接建立動態庫
<!--[if !vml]--><!--[endif]-->
<!--[if !vml]--><!--[endif]-->
<!--[if !vml]--><!--[endif]-->
編譯的時候需要jni相關的頭文件和庫文件,在vc6.0的的搜索路徑加入與java有關的兩個路徑即可即可
Tools->sptions->Directories
<!--[if !vml]--><!--[endif]-->
因爲必須在 Java 虛擬機中執行所有 Java 代碼,所以需要使用 Java 運行時環境。
完成這一步的方法之一是使用 java,它是隨 SDK 一起提供的 Java 解釋器。
所使用的命令是:
java -cp . test.test_JNI_javaCallc_test
或者直接在eclipose中運行即可
【教程七】從 C/C++ 程序調用 Java 代碼
JNI允許您從本機代碼內調用 Java 類方法。
要做到這一點,通常必須使用 Invocation API 在本機代碼內創建和初始化一個 JVM。
下列是您可能決定從 C/C++ 代碼調用Java 代碼的典型情況:
1.希望實現的這部分代碼是平臺無關的,它將用於跨多種平臺使用的功能。
2.需要在本機應用程序中訪問用 Java 語言編寫的代碼或代碼庫。
3.希望從本機代碼利用標準 Java 類庫。
從C/C++ 程序調用 Java代碼的四個步驟:
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->
1.編寫 Java 代碼。
這個步驟包含編寫一個或多個 Java 類,這些類實現(或調用其它方法實現)您想要訪問的功能。
2.編譯 Java 代碼。
在能夠使用這些 Java 類之前,必須成功地將它們編譯成字節碼。
3.編寫 C/C++ 代碼。
這個代碼將創建和實例化 JVM,並調用正確的 Java 方法。
4.運行本機 C/C++ 應用程序。
將運行應用程序以查看它是否正常工作。我們還將討論一些用於處理常見錯誤的技巧。
步驟 1:編寫Java 代碼
我們從編寫一個或多個 Java 源代碼文件開始,這些文件將實現我們想要本機 C/C++ 代碼使用的功能。
下面顯示了一個 Java 代碼示例JNI_cCalljava_test.java:
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->
[java] view plaincopy
<!--[if !supportLists]-->1. <!--[endif]-->package test;
<!--[if !supportLists]-->2. <!--[endif]-->
<!--[if !supportLists]-->3. <!--[endif]-->public class JNI_cCalljava_test {
<!--[if !supportLists]-->4. <!--[endif]-->
<!--[if !supportLists]-->5. <!--[endif]--> public static int intMethod(int n) {
<!--[if !supportLists]-->6. <!--[endif]--> return n*n;
<!--[if !supportLists]-->7. <!--[endif]--> }
<!--[if !supportLists]-->8. <!--[endif]-->
<!--[if !supportLists]-->9. <!--[endif]--> public static boolean booleanMethod(boolean bool) {
<!--[if !supportLists]-->10. <!--[endif]--> return !bool;
<!--[if !supportLists]-->11. <!--[endif]--> }
<!--[if !supportLists]-->12. <!--[endif]-->
<!--[if !supportLists]-->13. <!--[endif]-->}
注:JNI_cCalljava_test.java 實現了兩個 static Java 方法:intMethod(intn) 和booleanMethod(boolean
bool)(分別在第 3 行和第 7 行)。static方法是一種不需要與對象實例關聯的類方法。調用 static方法要更容易些,因爲不必實例化對象來調用它們。
步驟 2:編譯Java 代碼
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->
接下來,我們將 Java 代碼編譯成字節碼。
完成這一步的方法之一是使用隨SDK 一起提供的Java 編譯器 javac。使用的命令是:
JNI_cCalljava_test.java
或者直接在eclipose中編寫保存即可
步驟 3:編寫 C/C++ 代碼
即使是在本機應用程序中運行,所有 Java 字節碼也必須在 JVM 中執行。
因此 C/C++ 應用程序必須包含用來創建和初始化 JVM 的調用。
爲了方便我們,SDK 包含了作爲共享庫文件(jvm.dll 或 jvm.so)的 JVM,這個庫文件可以嵌入到本機應用程序中。
讓我們先從瀏覽一下 C 和 C++ 應用程序的整個代碼開始,然後對兩者進行比較。
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->
帶有嵌入式 JVM的 C 應用程序:
[cpp] view plaincopy
<!--[if !supportLists]-->1. <!--[endif]-->#include <jni.h>
<!--[if !supportLists]-->2. <!--[endif]-->//jni.h文件包含在 C 代碼中所需要的 JNI 的所有類型和函數定義
<!--[if !supportLists]-->3. <!--[endif]-->#ifdef _WIN32
<!--[if !supportLists]-->4. <!--[endif]-->#define PATH_SEPARATOR ';'
<!--[if !supportLists]-->5. <!--[endif]-->#else
<!--[if !supportLists]-->6. <!--[endif]-->#define PATH_SEPARATOR ':'
<!--[if !supportLists]-->7. <!--[endif]-->#endif
<!--[if !supportLists]-->8. <!--[endif]-->//1.包括準備本機應用程序以處理 Java 代碼
<!--[if !supportLists]-->9. <!--[endif]-->//2.將 JVM 嵌入本機應用程序
<!--[if !supportLists]-->10. <!--[endif]-->//3.然後從該應用程序內找到並調用 Java 方法。
<!--[if !supportLists]-->11. <!--[endif]-->int main()
<!--[if !supportLists]-->12. <!--[endif]-->{
<!--[if !supportLists]-->13. <!--[endif]-->/*
<!--[if !supportLists]-->14. <!--[endif]-->接下來,聲明所有希望在程序中使用的變量。
<!--[if !supportLists]-->15. <!--[endif]-->JavaVMOption options[] 具有用於 JVM 的各種選項設置。
<!--[if !supportLists]-->16. <!--[endif]-->當聲明變量時,確保所聲明的JavaVMOption options[] 數組足夠大,以便能容納您希望使用的所有選項。
<!--[if !supportLists]-->17. <!--[endif]-->在本例中,我們使用的唯一選項就是類路徑選項。
<!--[if !supportLists]-->18. <!--[endif]-->因爲在本示例中,我們所有的文件都在同一目錄中,所以將類路徑設置成當前目錄。
<!--[if !supportLists]-->19. <!--[endif]-->可以設置類路徑,使它指向任何您希望使用的目錄結構。*/
<!--[if !supportLists]-->20. <!--[endif]--> JavaVMOption options[1];
<!--[if !supportLists]-->21. <!--[endif]--> JNIEnv *env;
<!--[if !supportLists]-->22. <!--[endif]--> JavaVM *jvm;
<!--[if !supportLists]-->23. <!--[endif]--> JavaVMInitArgs vm_args;
<!--[if !supportLists]-->24. <!--[endif]-->/*JNIEnv *env 表示 JNI 執行環境。
<!--[if !supportLists]-->25. <!--[endif]-->JavaVM jvm 是指向 JVM 的指針,我們主要使用這個指針來創建、初始化和銷燬 JVM。
<!--[if !supportLists]-->26. <!--[endif]-->JavaVMInitArgs vm_args 表示可以用來初始化 JVM 的各種 JVM 參數。*/
<!--[if !supportLists]-->27. <!--[endif]-->
<!--[if !supportLists]-->28. <!--[endif]--> long status;
<!--[if !supportLists]-->29. <!--[endif]--> jclass cls;
<!--[if !supportLists]-->30. <!--[endif]--> jmethodID mid;
<!--[if !supportLists]-->31. <!--[endif]--> jint square;
<!--[if !supportLists]-->32. <!--[endif]--> jboolean not;
<!--[if !supportLists]-->33. <!--[endif]-->
<!--[if !supportLists]-->34. <!--[endif]-->/*avaVMInitArgs 結構表示用於 JVM 的初始化參數。
<!--[if !supportLists]-->35. <!--[endif]-->在執行 Java 代碼之前,可以使用這些參數來定製運行時環境。
<!--[if !supportLists]-->36. <!--[endif]-->正如您所見,這些選項是一個參數,而 Java 版本是另一個參數。
<!--[if !supportLists]-->37. <!--[endif]-->按如下所示設置了這些參數:*/
<!--[if !supportLists]-->38. <!--[endif]-->
<!--[if !supportLists]-->39. <!--[endif]-->/*爲 JVM 設置類路徑,以使它能找到所需要的 Java 類。
<!--[if !supportLists]-->40. <!--[endif]-->在這個特定示例中,因爲 Sample2.class 和Sample2.exe 都位於同一目錄中,所以將類路徑設置成當前目錄。
<!--[if !supportLists]-->41. <!--[endif]-->我們用來爲 Sample2.c 設置類路徑的代碼如下所示:*/
<!--[if !supportLists]-->42. <!--[endif]--> options[0].optionString = "-Djava.class.path=.";
<!--[if !supportLists]-->43. <!--[endif]--> memset(&vm_args, 0, sizeof(vm_args));
<!--[if !supportLists]-->44. <!--[endif]--> vm_args.version = JNI_VERSION_1_2;
<!--[if !supportLists]-->45. <!--[endif]--> vm_args.nOptions = 1;
<!--[if !supportLists]-->46. <!--[endif]--> vm_args.options = options;
<!--[if !supportLists]-->47. <!--[endif]-->
<!--[if !supportLists]-->48. <!--[endif]-->/*創建 JVM
<!--[if !supportLists]-->49. <!--[endif]-->處理完所有設置之後,現在就準備創建 JVM 了。先從調用方法開始
<!--[if !supportLists]-->50. <!--[endif]-->如果成功,則這個方法返回零,否則,如果無法創建 JVM,則返回JNI_ERR。*/
<!--[if !supportLists]-->51. <!--[endif]--> status = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
<!--[if !supportLists]-->52. <!--[endif]-->
<!--[if !supportLists]-->53. <!--[endif]--> if (status != JNI_ERR)
<!--[if !supportLists]-->54. <!--[endif]--> {
<!--[if !supportLists]-->55. <!--[endif]-->/*
<!--[if !supportLists]-->56. <!--[endif]-->查找並裝入 Java 類
<!--[if !supportLists]-->57. <!--[endif]-->一旦創建了 JVM 之後,就可以準備開始在本機應用程序中運行 Java 代碼。
<!--[if !supportLists]-->58. <!--[endif]-->首先,需要使用FindClass() 函數查找並裝入 Java 類,如下所示:
<!--[if !supportLists]-->59. <!--[endif]-->cls 變量存儲執行FindClass() 函數後的結果,如果找到該類,則 cls 變量表示該Java 類的句柄,
<!--[if !supportLists]-->60. <!--[endif]-->如果不能找到該類,則 cls 將爲零。
<!--[if !supportLists]-->61. <!--[endif]-->*/
<!--[if !supportLists]-->62. <!--[endif]--> cls = (*env)->FindClass(env, "test/JNI_cCalljava_test");
<!--[if !supportLists]-->63. <!--[endif]--> printf("test1,cls=%d...\n",cls);
<!--[if !supportLists]-->64. <!--[endif]-->
<!--[if !supportLists]-->65. <!--[endif]--> if(cls !=0)
<!--[if !supportLists]-->66. <!--[endif]--> {
<!--[if !supportLists]-->67. <!--[endif]-->/*
<!--[if !supportLists]-->68. <!--[endif]-->查找 Java 方法
<!--[if !supportLists]-->69. <!--[endif]-->接下來,我們希望用 GetStaticMethodID() 函數在該類中查找某個方法。
<!--[if !supportLists]-->70. <!--[endif]-->我們希望查找方法 intMethod,它接收一個 int 參數並返回一個 int。
<!--[if !supportLists]-->71. <!--[endif]-->以下是查找 intMethod 的代碼:
<!--[if !supportLists]-->72. <!--[endif]-->*/
<!--[if !supportLists]-->73. <!--[endif]--> mid = (*env)->GetStaticMethodID(env, cls, "intMethod", "(I)I");
<!--[if !supportLists]-->74. <!--[endif]-->/*
<!--[if !supportLists]-->75. <!--[endif]-->mid 變量存儲執行 GetStaticMethodID() 函數後的結果。
<!--[if !supportLists]-->76. <!--[endif]-->如果找到了該方法,則 mid 變量表示該方法的句柄。
<!--[if !supportLists]-->77. <!--[endif]-->如果不能找到該方法,則mid 將爲零。
<!--[if !supportLists]-->78. <!--[endif]-->*/
<!--[if !supportLists]-->79. <!--[endif]--> if(mid !=0)
<!--[if !supportLists]-->80. <!--[endif]--> {
<!--[if !supportLists]-->81. <!--[endif]-->/*CallStaticIntMethod() 方法接受 cls(表示類)、mid(表示方法)以及用於該方法一個或多個參數。
<!--[if !supportLists]-->82. <!--[endif]-->在本例中參數是 int 5。*/
<!--[if !supportLists]-->83. <!--[endif]--> square = (*env)->CallStaticIntMethod(env, cls, mid, 5);
<!--[if !supportLists]-->84. <!--[endif]--> printf("Result of intMethod: %d\n", square);
<!--[if !supportLists]-->85. <!--[endif]--> }
<!--[if !supportLists]-->86. <!--[endif]-->
<!--[if !supportLists]-->87. <!--[endif]--> mid = (*env)->GetStaticMethodID(env, cls, "booleanMethod", "(Z)Z");
<!--[if !supportLists]-->88. <!--[endif]--> if(mid !=0)
<!--[if !supportLists]-->89. <!--[endif]--> {
<!--[if !supportLists]-->90. <!--[endif]--> not = (*env)->CallStaticBooleanMethod(env, cls, mid, 1);
<!--[if !supportLists]-->91. <!--[endif]--> printf("Result of booleanMethod: %d\n", not);
<!--[if !supportLists]-->92. <!--[endif]--> }
<!--[if !supportLists]-->93. <!--[endif]--> }
<!--[if !supportLists]-->94. <!--[endif]-->
<!--[if !supportLists]-->95. <!--[endif]--> (*jvm)->DestroyJavaVM(jvm);
<!--[if !supportLists]-->96. <!--[endif]--> return 0;
<!--[if !supportLists]-->97. <!--[endif]--> }
<!--[if !supportLists]-->98. <!--[endif]--> else
<!--[if !supportLists]-->99. <!--[endif]--> return -1;
<!--[if !supportLists]-->100. <!--[endif]-->}
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->
帶有嵌入式 JVM的 C++ 應用程序
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->
[cpp] view plaincopy
<!--[if !supportLists]-->1. <!--[endif]-->#include <jni.h>
<!--[if !supportLists]-->2. <!--[endif]-->
<!--[if !supportLists]-->3. <!--[endif]-->#ifdef _WIN32
<!--[if !supportLists]-->4. <!--[endif]-->#define PATH_SEPARATOR ';'
<!--[if !supportLists]-->5. <!--[endif]-->#else
<!--[if !supportLists]-->6. <!--[endif]-->#define PATH_SEPARATOR ':'
<!--[if !supportLists]-->7. <!--[endif]-->#endif
<!--[if !supportLists]-->8. <!--[endif]-->
<!--[if !supportLists]-->9. <!--[endif]-->int main()
<!--[if !supportLists]-->10. <!--[endif]-->{
<!--[if !supportLists]-->11. <!--[endif]--> JavaVMOption options[1];
<!--[if !supportLists]-->12. <!--[endif]--> JNIEnv *env;
<!--[if !supportLists]-->13. <!--[endif]--> JavaVM *jvm;
<!--[if !supportLists]-->14. <!--[endif]--> JavaVMInitArgs vm_args;
<!--[if !supportLists]-->15. <!--[endif]--> long status;
<!--[if !supportLists]-->16. <!--[endif]--> jclass cls;
<!--[if !supportLists]-->17. <!--[endif]--> jmethodID mid;
<!--[if !supportLists]-->18. <!--[endif]--> jint square;
<!--[if !supportLists]-->19. <!--[endif]--> jboolean not;
<!--[if !supportLists]-->20. <!--[endif]-->
<!--[if !supportLists]-->21. <!--[endif]--> options[0].optionString = "-Djava.class.path=.";
<!--[if !supportLists]-->22. <!--[endif]--> memset(&vm_args, 0, sizeof(vm_args));
<!--[if !supportLists]-->23. <!--[endif]--> vm_args.version = JNI_VERSION_1_2;
<!--[if !supportLists]-->24. <!--[endif]--> vm_args.nOptions = 1;
<!--[if !supportLists]-->25. <!--[endif]--> vm_args.options = options;
<!--[if !supportLists]-->26. <!--[endif]--> status = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
<!--[if !supportLists]-->27. <!--[endif]-->
<!--[if !supportLists]-->28. <!--[endif]--> if (status != JNI_ERR)
<!--[if !supportLists]-->29. <!--[endif]--> {
<!--[if !supportLists]-->30. <!--[endif]--> cls = env->FindClass("Sample2");
<!--[if !supportLists]-->31. <!--[endif]--> if(cls !=0)
<!--[if !supportLists]-->32. <!--[endif]--> {
<!--[if !supportLists]-->33. <!--[endif]--> mid = env->GetStaticMethodID(cls, "intMethod", "(I)I");
<!--[if !supportLists]-->34. <!--[endif]--> if(mid !=0)
<!--[if !supportLists]-->35. <!--[endif]--> {
<!--[if !supportLists]-->36. <!--[endif]--> square = env->CallStaticIntMethod(cls, mid, 5);
<!--[if !supportLists]-->37. <!--[endif]--> printf("Result of intMethod: %d\n", square);
<!--[if !supportLists]-->38. <!--[endif]--> }
<!--[if !supportLists]-->39. <!--[endif]-->
<!--[if !supportLists]-->40. <!--[endif]--> mid = env->GetStaticMethodID(cls, "booleanMethod", "(Z)Z")
<!--[if !supportLists]-->41. <!--[endif]--> if(mid !=0)
<!--[if !supportLists]-->42. <!--[endif]--> {
<!--[if !supportLists]-->43. <!--[endif]--> not = env->CallStaticBooleanMethod(cls, mid, 1);
<!--[if !supportLists]-->44. <!--[endif]--> printf("Result of booleanMethod: %d\n", not);
<!--[if !supportLists]-->45. <!--[endif]--> }
<!--[if !supportLists]-->46. <!--[endif]--> }
<!--[if !supportLists]-->47. <!--[endif]-->
<!--[if !supportLists]-->48. <!--[endif]--> jvm->DestroyJavaVM();
<!--[if !supportLists]-->49. <!--[endif]--> return 0;
<!--[if !supportLists]-->50. <!--[endif]--> }
<!--[if !supportLists]-->51. <!--[endif]--> else
<!--[if !supportLists]-->52. <!--[endif]--> return -1;
<!--[if !supportLists]-->53. <!--[endif]--> }
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->
C 和 C++ 實現的比較
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->
C 和C++ 代碼幾乎相同;唯一的差異在於用來訪問 JNI 函數的方法。
在 C 中,爲了取出函數指針所引用的值,JNI 函數調用前要加一個(*env)-> 前綴。
在 C++ 中,JNIEnv類擁有處理函數指針查找的內聯成員函數。
因此,雖然這兩行代碼訪問同一函數,但每種語言都有各自的語法,如下所示。
C 語法:
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->
cls = (*env)->FindClass(env, "Sample2");
C++ 語法:
cls = env->FindClass("Sample2");
C 語法:
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->
mid = (*env)->GetStaticMethodID(env, cls, "intMethod", "(I)I");
C++ 語法:
mid = env->GetStaticMethodID(cls, "intMethod", "(I)I");
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->
C 語法:
square = env->CallStaticIntMethod(cls, mid, 5);
C++ 語法:
square = (*env)->CallStaticIntMethod(env, cls, mid, 5);
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->
C 語法:
(*jvm)->DestroyJavaVM(jvm);
C++ 語法:
jvm->DestroyJavaVM();
步驟 4:運行應用程序
現在準備運行這個 C 應用程序,並確保代碼正常工作。當運行 Sample2.exe 時,應該可以得到如下結果:
windows:
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->
使用vc6.0建一個普通的C語言工程
頭文件路徑設置同Java調用C語言裏的設置
連接時需要jvm.lib支持
這裏需要右擊建立的工程,單擊設置(Settings),link選項欄將數據庫路徑添加進來
<!--[if !vml]--><!--[endif]--> <!--[if !vml]--><!--[endif]-->
C:"\Program Files"\Java\jdk1.6.0_10\lib\jvm.lib
在下面的project options中加入以上語句,用空格隔開,programe Files用雙引號引起來
運行時需要jvm.dll動態庫的支持,需要在系統環境變量中增加以下路徑:
C:\Program Files\Java\jdk1.6.0_10\jre\bin\server
方法:右擊 我的電腦-》屬性-》高級-》環境變量-》PATH 編輯,在原有環境變量的基礎上增加以上路徑,注意用";"號隔開
將eclipose生成的java代碼放在JNI_cCalljava_test.exe同目錄下(注意按照把報名文件夾也拷過去)
以上教程由凌陽教育Android培訓講師-徐哥提供,凌陽教育專注於嵌入式Linux培訓,Android培訓領域,擁有十多年的高校嵌入式師資培訓經驗,成爲中國高校嵌入式培訓第一品牌!
歡迎訪問凌陽教育官方網站:http://www.sunplusedu.com