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: - package test;
-
- public class JNI_cCalljava_test {
-
- public static int intMethod(int n) {
- return n*n;
- }
-
- public static boolean booleanMethod(boolean bool) {
- return !bool;
- }
-
- }
註:JNI_cCalljava_test.java 實現了兩個 static Java 方法:intMethod(intn) 和 booleanMethod(boolean bool)(分別在第 3 行和第 7 行)。static方法是一種不需要與對象實例關聯的類方法。調用 static方法要更容易些,因為不必實例化對象來調用它們。
步驟 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 應用程序:
- #include
-
- #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(env, "test/JNI_cCalljava_test");
- printf("test1,cls=%d...\n",cls);
-
- if(cls !=0)
- {
-
-
-
-
-
-
- mid = (*env)->GetStaticMethodID(env, cls, "intMethod", "(I)I");
-
-
-
-
-
- if(mid !=0)
- {
-
-
- 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++ 應用程序 - #include
-
- #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++ 語法: