c/c++呼叫JAVA
JNI允許您從本機程式碼內呼叫 Java 類方法。
要做到這一點,通常必須使用 Invocation API 在本機程式碼內建立和初始化一個 JVM。
下列是您可能決定從 C/C++ 程式碼呼叫Java 程式碼的典型情況:
1.希望實現的這部分程式碼是平臺無關的,它將用於跨多種平臺使用的功能。
2.需要在本機應用程式中訪問用 Java 語言編寫的程式碼或程式碼庫。
3.希望從本機程式碼利用標準 Java 類庫。
從C/C++ 程式呼叫 Java 程式碼的四個步驟:1.編寫 Java 程式碼。
這個步驟包含編寫一個或多個 Java 類,這些類實現(或呼叫其它方法實現)您想要訪問的功能。
2.編譯 Java 程式碼。
在能夠使用這些 Java 類之前,必須成功地將它們編譯成位元組碼。
3.編寫 C/C++ 程式碼。
這個程式碼將建立和例項化 JVM,並呼叫正確的 Java 方法。
4.執行本機 C/C++ 應用程式。
將執行應用程式以檢視它是否正常工作。我們還將討論一些用於處理常見錯誤的技巧。步驟 1:編寫Java 程式碼
我們從編寫一個或多個 Java 原始碼檔案開始,這些檔案將實現我們想要本機 C/C++ 程式碼使用的功能。
下面顯示了一個 Java 程式碼示例JNI_cCalljava_test.java:
[java] view plain copy print
- package test;
- publicclass JNI_cCalljava_test {
- publicstaticint intMethod(int n) {
- return n*n;
- }
- publicstaticboolean booleanMethod(boolean bool) {
- return !bool;
- }
- }
注:JNI_cCalljava_test.java 實現了兩個 static Java 方法:intMethod(intn) 和 booleanMethod(boolean bool)(分別在第 3 行和第 7 行)。static方法是一種不需要與物件例項關聯的類方法。呼叫 static方法要更容易些,因為不必例項化物件來呼叫它們。package test; public class JNI_cCalljava_test { public static int intMethod(int n) { return n*n; } public static boolean booleanMethod(boolean bool) { return !bool; } }
步驟 2:編譯Java 程式碼
接下來,我們將 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++ 應用程式的整個程式碼開始,然後對兩者進行比較。帶有嵌入式 JVM的 C 應用程式:
[cpp] view plain copy print?- #include <jni.h>
- //jni.h檔案包含在 C 程式碼中所需要的 JNI 的所有型別和函式定義
- #ifdef _WIN32
- #define PATH_SEPARATOR ';'
- #else
- #define PATH_SEPARATOR ':'
- #endif
- //1.包括準備本機應用程式以處理 Java 程式碼
- //2.將 JVM 嵌入本機應用程式
- //3.然後從該應用程式內找到並呼叫 Java 方法。
- int main()
- {
- /*
- 接下來,宣告所有希望在程式中使用的變數。
- JavaVMOption options[] 具有用於 JVM 的各種選項設定。
- 當宣告變數時,確保所宣告的JavaVMOption options[] 陣列足夠大,以便能容納您希望使用的所有選項。
- 在本例中,我們使用的唯一選項就是類路徑選項。
- 因為在本示例中,我們所有的檔案都在同一目錄中,所以將類路徑設定成當前目錄。
- 可以設定類路徑,使它指向任何您希望使用的目錄結構。*/
- JavaVMOption options[1];
- JNIEnv *env;
- JavaVM *jvm;
- JavaVMInitArgs vm_args;
- /*JNIEnv *env 表示 JNI 執行環境。
- JavaVM jvm 是指向 JVM 的指標,我們主要使用這個指標來建立、初始化和銷燬 JVM。
- JavaVMInitArgs vm_args 表示可以用來初始化 JVM 的各種 JVM 引數。*/
- long status;
- jclass cls;
- jmethodID mid;
- jint square;
- jboolean not;
- /*avaVMInitArgs 結構表示用於 JVM 的初始化引數。
- 在執行 Java 程式碼之前,可以使用這些引數來定製執行時環境。
- 正如您所見,這些選項是一個引數,而 Java 版本是另一個引數。
- 按如下所示設定了這些引數:*/
- /*為 JVM 設定類路徑,以使它能找到所需要的 Java 類。
- 在這個特定示例中,因為 Sample2.class 和Sample2.exe 都位於同一目錄中,所以將類路徑設定成當前目錄。
- 我們用來為 Sample2.c 設定類路徑的程式碼如下所示:*/
- options[0].optionString = "-Djava.class.path=.";
- memset(&vm_args, 0, sizeof(vm_args));
- vm_args.version = JNI_VERSION_1_2;
- vm_args.nOptions = 1;
- vm_args.options = options;
- /*建立 JVM
- 處理完所有設定之後,現在就準備建立 JVM 了。先從呼叫方法開始
- 如果成功,則這個方法返回零,否則,如果無法建立 JVM,則返回JNI_ERR。*/
- status = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
- if (status != JNI_ERR)
- {
- /*
- 查詢並裝入 Java 類
- 一旦建立了 JVM 之後,就可以準備開始在本機應用程式中執行 Java 程式碼。
- 首先,需要使用FindClass() 函式查詢並裝入 Java 類,如下所示:
- cls 變數儲存執行FindClass() 函式後的結果,如果找到該類,則 cls 變量表示該Java 類的控制代碼,
- 如果不能找到該類,則 cls 將為零。
- */
- cls = (*env)->FindClass(env, "test/JNI_cCalljava_test");
- printf("test1,cls=%d...\n",cls);
- if(cls !=0)
- {
- /*
- 查詢 Java 方法
- 接下來,我們希望用 GetStaticMethodID() 函式在該類中查詢某個方法。
- 我們希望查詢方法 intMethod,它接收一個 int 引數並返回一個 int。
- 以下是查詢 intMethod 的程式碼:
- */
- mid = (*env)->GetStaticMethodID(env, cls, "intMethod", "(I)I");
- /*
- mid 變數儲存執行 GetStaticMethodID() 函式後的結果。
- 如果找到了該方法,則 mid 變量表示該方法的控制代碼。
- 如果不能找到該方法,則mid 將為零。
- */
- if(mid !=0)
- {
- /*CallStaticIntMethod() 方法接受 cls(表示類)、mid(表示方法)以及用於該方法一個或多個引數。
- 在本例中引數是 int 5。*/
- square = (*env)->CallStaticIntMethod(env, cls, mid, 5);
- printf("Result of intMethod: %d\n", square);
- }
- mid = (*env)->GetStaticMethodID(env, cls, "booleanMethod", "(Z)Z");
- if(mid !=0)
- {
- not = (*env)->CallStaticBooleanMethod(env, cls, mid, 1);
- printf("Result of booleanMethod: %d\n", not);
- }
- }
- (*jvm)->DestroyJavaVM(jvm);
- return 0;
- }
- else
- return -1;
- }
#include <jni.h>
//jni.h檔案包含在 C 程式碼中所需要的 JNI 的所有型別和函式定義
#ifdef _WIN32
#define PATH_SEPARATOR ';'
#else
#define PATH_SEPARATOR ':'
#endif
//1.包括準備本機應用程式以處理 Java 程式碼
//2.將 JVM 嵌入本機應用程式
//3.然後從該應用程式內找到並呼叫 Java 方法。
int main()
{
/*
接下來,宣告所有希望在程式中使用的變數。
JavaVMOption options[] 具有用於 JVM 的各種選項設定。
當宣告變數時,確保所宣告的JavaVMOption options[] 陣列足夠大,以便能容納您希望使用的所有選項。
在本例中,我們使用的唯一選項就是類路徑選項。
因為在本示例中,我們所有的檔案都在同一目錄中,所以將類路徑設定成當前目錄。
可以設定類路徑,使它指向任何您希望使用的目錄結構。*/
JavaVMOption options[1];
JNIEnv *env;
JavaVM *jvm;
JavaVMInitArgs vm_args;
/*JNIEnv *env 表示 JNI 執行環境。
JavaVM jvm 是指向 JVM 的指標,我們主要使用這個指標來建立、初始化和銷燬 JVM。
JavaVMInitArgs vm_args 表示可以用來初始化 JVM 的各種 JVM 引數。*/
long status;
jclass cls;
jmethodID mid;
jint square;
jboolean not;
/*avaVMInitArgs 結構表示用於 JVM 的初始化引數。
在執行 Java 程式碼之前,可以使用這些引數來定製執行時環境。
正如您所見,這些選項是一個引數,而 Java 版本是另一個引數。
按如下所示設定了這些引數:*/
/*為 JVM 設定類路徑,以使它能找到所需要的 Java 類。
在這個特定示例中,因為 Sample2.class 和Sample2.exe 都位於同一目錄中,所以將類路徑設定成當前目錄。
我們用來為 Sample2.c 設定類路徑的程式碼如下所示:*/
options[0].optionString = "-Djava.class.path=.";
memset(&vm_args, 0, sizeof(vm_args));
vm_args.version = JNI_VERSION_1_2;
vm_args.nOptions = 1;
vm_args.options = options;
/*建立 JVM
處理完所有設定之後,現在就準備建立 JVM 了。先從呼叫方法開始
如果成功,則這個方法返回零,否則,如果無法建立 JVM,則返回JNI_ERR。*/
status = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
if (status != JNI_ERR)
{
/*
查詢並裝入 Java 類
一旦建立了 JVM 之後,就可以準備開始在本機應用程式中執行 Java 程式碼。
首先,需要使用FindClass() 函式查詢並裝入 Java 類,如下所示:
cls 變數儲存執行FindClass() 函式後的結果,如果找到該類,則 cls 變量表示該Java 類的控制代碼,
如果不能找到該類,則 cls 將為零。
*/
cls = (*env)->FindClass(env, "test/JNI_cCalljava_test");
printf("test1,cls=%d...\n",cls);
if(cls !=0)
{
/*
查詢 Java 方法
接下來,我們希望用 GetStaticMethodID() 函式在該類中查詢某個方法。
我們希望查詢方法 intMethod,它接收一個 int 引數並返回一個 int。
以下是查詢 intMethod 的程式碼:
*/
mid = (*env)->GetStaticMethodID(env, cls, "intMethod", "(I)I");
/*
mid 變數儲存執行 GetStaticMethodID() 函式後的結果。
如果找到了該方法,則 mid 變量表示該方法的控制代碼。
如果不能找到該方法,則mid 將為零。
*/
if(mid !=0)
{
/*CallStaticIntMethod() 方法接受 cls(表示類)、mid(表示方法)以及用於該方法一個或多個引數。
在本例中引數是 int 5。*/
square = (*env)->CallStaticIntMethod(env, cls, mid, 5);
printf("Result of intMethod: %d\n", square);
}
mid = (*env)->GetStaticMethodID(env, cls, "booleanMethod", "(Z)Z");
if(mid !=0)
{
not = (*env)->CallStaticBooleanMethod(env, cls, mid, 1);
printf("Result of booleanMethod: %d\n", not);
}
}
(*jvm)->DestroyJavaVM(jvm);
return 0;
}
else
return -1;
}
帶有嵌入式 JVM的 C++ 應用程式
[cpp] view plain copy print?
- #include <jni.h>
- #ifdef _WIN32
- #define PATH_SEPARATOR ';'
- #else
- #define PATH_SEPARATOR ':'
- #endif
- int main()
- {
- JavaVMOption options[1];
- JNIEnv *env;
- JavaVM *jvm;
- JavaVMInitArgs vm_args;
- long status;
- jclass cls;
- jmethodID mid;
- jint square;
- jboolean not;
- options[0].optionString = "-Djava.class.path=.";
- memset(&vm_args, 0, sizeof(vm_args));
- vm_args.version = JNI_VERSION_1_2;
- vm_args.nOptions = 1;
- vm_args.options = options;
- status = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
- if (status != JNI_ERR)
- {
- cls = env->FindClass("Sample2");
- if(cls !=0)
- {
- mid = env->GetStaticMethodID(cls, "intMethod", "(I)I");
- if(mid !=0)
- {
- square = env->CallStaticIntMethod(cls, mid, 5);
- printf("Result of intMethod: %d\n", square);
- }
- mid = env->GetStaticMethodID(cls, "booleanMethod", "(Z)Z")
- if(mid !=0)
- {
- not = env->CallStaticBooleanMethod(cls, mid, 1);
- printf("Result of booleanMethod: %d\n", not);
- }
- }
- jvm->DestroyJavaVM();
- return 0;
- }
- else
- return -1;
- }
#include <jni.h>
#ifdef _WIN32
#define PATH_SEPARATOR ';'
#else
#define PATH_SEPARATOR ':'
#endif
int main()
{
JavaVMOption options[1];
JNIEnv *env;
JavaVM *jvm;
JavaVMInitArgs vm_args;
long status;
jclass cls;
jmethodID mid;
jint square;
jboolean not;
options[0].optionString = "-Djava.class.path=.";
memset(&vm_args, 0, sizeof(vm_args));
vm_args.version = JNI_VERSION_1_2;
vm_args.nOptions = 1;
vm_args.options = options;
status = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
if (status != JNI_ERR)
{
cls = env->FindClass("Sample2");
if(cls !=0)
{
mid = env->GetStaticMethodID(cls, "intMethod", "(I)I");
if(mid !=0)
{
square = env->CallStaticIntMethod(cls, mid, 5);
printf("Result of intMethod: %d\n", square);
}
mid = env->GetStaticMethodID(cls, "booleanMethod", "(Z)Z")
if(mid !=0)
{
not = env->CallStaticBooleanMethod(cls, mid, 1);
printf("Result of booleanMethod: %d\n", not);
}
}
jvm->DestroyJavaVM();
return 0;
}
else
return -1;
}
C 和 C++ 實現的比較
C 和C++ 程式碼幾乎相同;唯一的差異在於用來訪問 JNI 函式的方法。
在 C 中,為了取出函式指標所引用的值,JNI 函式呼叫前要加一個(*env)-> 字首。
在 C++ 中,JNIEnv類擁有處理函式指標查詢的內聯成員函式。
因此,雖然這兩行程式碼訪問同一函式,但每種語言都有各自的語法,如下所示。
C 語法:cls = (*env)->FindClass(env, "Sample2");
C++ 語法:
cls = env->FindClass("Sample2");
C 語法:mid = (*env)->GetStaticMethodID(env, cls, "intMethod", "(I)I");
C++ 語法:
mid = env->GetStaticMethodID(cls, "intMethod", "(I)I");C 語法:
square = env->CallStaticIntMethod(cls, mid, 5);
C++ 語法:
square = (*env)->CallStaticIntMethod(env, cls, mid, 5);
C 語法:
(*jvm)->DestroyJavaVM(jvm);
C++ 語法:
jvm->DestroyJavaVM();
步驟 4:執行應用程式
現在準備執行這個 C 應用程式,並確保程式碼正常工作。當執行 Sample2.exe 時,應該可以得到如下結果:
windows:使用vc6.0建一個普通的C語言工程
標頭檔案路徑設定同Java呼叫C語言裡的設定
連線時需要jvm.lib支援
這裡需要右擊建立的工程,單擊設定(Settings),link選項欄將資料庫路徑新增進來
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同目錄下(注意按照把報名資料夾也拷過去)
E:\Sample2>JNI_cCalljava_test.exeResult of intMethod: 25
Result of booleanMethod: 0