從 C/C++ 程式呼叫 Java 程式碼

工程師WWW發表於2015-09-02

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:
  1. //package test;  //打包會導致找不到類
  2.   
  3. public class JNI_cCalljava_test {  
  4.       
  5.    public static int intMethod(int n) {  
  6.           return n*n;  
  7.       }  
  8.   
  9.     public static boolean booleanMethod(boolean bool) {  
  10.      return !bool;  
  11.     }  
  12.       
  13. }  

注:JNI_cCalljava_test.java 實現了兩個 static Java 方法:intMethod(intn) 和 booleanMethod(boolean bool)(分別在第 3 行和第 7 行)。static方法是一種不需要與物件例項關聯的類方法。呼叫 static方法要更容易些,因為不必例項化物件來呼叫它們。

步驟 2:編譯Java 程式碼

接下來,我們將 Java 程式碼編譯成位元組碼。

完成這一步的方法之一是使用隨SDK 一起提供的Java 編譯器 javac。使用的命令是:

javac JNI_cCalljava_test.java

或者直接在eclipose中編寫儲存即可

步驟 3:編寫 C/C++ 程式碼

即使是在本機應用程式中執行,所有 Java 位元組碼也必須在 JVM 中執行。

因此 C/C++ 應用程式必須包含用來建立和初始化 JVM 的呼叫。

為了方便我們,SDK 包含了作為共享庫檔案(jvm.dll 或 jvm.so)的 JVM,這個庫檔案可以嵌入到本機應用程式中。


讓我們先從瀏覽一下 C 和 C++ 應用程式的整個程式碼開始,然後對兩者進行比較。

帶有嵌入式 JVM的 C 應用程式:

  1. #include <jni.h>   
  2. //jni.h檔案包含在 C 程式碼中所需要的 JNI 的所有型別和函式定義   
  3. #ifdef _WIN32   
  4. #define PATH_SEPARATOR ';'   
  5. #else   
  6. #define PATH_SEPARATOR ':'   
  7. #endif   
  8. //1.包括準備本機應用程式以處理 Java 程式碼   
  9. //2.將 JVM 嵌入本機應用程式   
  10. //3.然後從該應用程式內找到並呼叫 Java 方法。   
  11. int main()  
  12. {  
  13. /* 
  14. 接下來,宣告所有希望在程式中使用的變數。 
  15. JavaVMOption options[] 具有用於 JVM 的各種選項設定。 
  16. 當宣告變數時,確保所宣告的JavaVMOption options[] 陣列足夠大,以便能容納您希望使用的所有選項。 
  17. 在本例中,我們使用的唯一選項就是類路徑選項。 
  18. 因為在本示例中,我們所有的檔案都在同一目錄中,所以將類路徑設定成當前目錄。 
  19. 可以設定類路徑,使它指向任何您希望使用的目錄結構。*/  
  20.     JavaVMOption options[1];  
  21.     JNIEnv *env;  
  22.     JavaVM *jvm;  
  23.     JavaVMInitArgs vm_args;  
  24. /*JNIEnv *env          表示 JNI 執行環境。 
  25. JavaVM jvm             是指向 JVM 的指標,我們主要使用這個指標來建立、初始化和銷燬 JVM。 
  26. JavaVMInitArgs vm_args 表示可以用來初始化 JVM 的各種 JVM 引數。*/  
  27.       
  28.     long status;  
  29.     jclass cls;  
  30.     jmethodID mid;  
  31.     jint square;  
  32.     jboolean not;  
  33.   
  34. /*avaVMInitArgs 結構表示用於 JVM 的初始化引數。 
  35. 在執行 Java 程式碼之前,可以使用這些引數來定製執行時環境。 
  36. 正如您所見,這些選項是一個引數,而 Java 版本是另一個引數。 
  37. 按如下所示設定了這些引數:*/  
  38.   
  39. /*為 JVM 設定類路徑,以使它能找到所需要的 Java 類。 
  40. 在這個特定示例中,因為 Sample2.class 和Sample2.exe 都位於同一目錄中,所以將類路徑設定成當前目錄。 
  41. 我們用來為 Sample2.c 設定類路徑的程式碼如下所示:*/  
  42.     options[0].optionString = "-Djava.class.path=.";  
  43.     memset(&vm_args, 0, sizeof(vm_args));  
  44.     vm_args.version = JNI_VERSION_1_2;  
  45.     vm_args.nOptions = 1;  
  46.     vm_args.options = options;  
  47.       
  48. /*建立 JVM 
  49. 處理完所有設定之後,現在就準備建立 JVM 了。先從呼叫方法開始 
  50. 如果成功,則這個方法返回零,否則,如果無法建立 JVM,則返回JNI_ERR。*/  
  51.     status = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);  
  52.   
  53.     if (status != JNI_ERR)  
  54.     {  
  55. /* 
  56. 查詢並裝入 Java 類 
  57. 一旦建立了 JVM 之後,就可以準備開始在本機應用程式中執行 Java 程式碼。 
  58. 首先,需要使用FindClass() 函式查詢並裝入 Java 類,如下所示: 
  59. cls 變數儲存執行FindClass() 函式後的結果,如果找到該類,則 cls 變數表示該Java 類的控制程式碼, 
  60. 如果不能找到該類,則 cls 將為零。 
  61. */  
  62.         cls = (*env)->FindClass(env, "JNI_cCalljava_test");  //  "test/JNI_cCalljava_test"會找不到類,原因待查
  63. if (!cls)
    {
    printf("Class not found\n");
    return -1;
    }
  64.         printf("test1,cls=%d...\n",cls);  
  65.   
  66.         if(cls !=0)  
  67.         {   
  68. /* 
  69. 查詢 Java 方法 
  70. 接下來,我們希望用 GetStaticMethodID() 函式在該類中查詢某個方法。 
  71. 我們希望查詢方法 intMethod,它接收一個 int 引數並返回一個 int。 
  72. 以下是查詢 intMethod 的程式碼: 
  73. */  
  74.             mid = (*env)->GetStaticMethodID(env, cls, "intMethod""(I)I");  
  75. /* 
  76. mid 變數儲存執行 GetStaticMethodID() 函式後的結果。 
  77. 如果找到了該方法,則 mid 變數表示該方法的控制程式碼。 
  78. 如果不能找到該方法,則mid 將為零。 
  79. */  
  80.             if(mid !=0)  
  81.             {   
  82. /*CallStaticIntMethod() 方法接受 cls(表示類)、mid(表示方法)以及用於該方法一個或多個引數。 
  83. 在本例中引數是 int 5。*/  
  84.                 square = (*env)->CallStaticIntMethod(env, cls, mid, 5);  
  85.                 printf("Result of intMethod: %d\n", square);  
  86.             }  
  87.   
  88.             mid = (*env)->GetStaticMethodID(env, cls, "booleanMethod""(Z)Z");  
  89.             if(mid !=0)  
  90.             {  
  91.                 not = (*env)->CallStaticBooleanMethod(env, cls, mid, 1);  
  92.                 printf("Result of booleanMethod: %d\n", not);  
  93.             }  
  94.         }  
  95.   
  96.         (*jvm)->DestroyJavaVM(jvm);  
  97.         return 0;  
  98.     }  
  99.     else  
  100.     return -1;  
  101. }  
gcc -o CCallJava2 CCallJava2.c -I/usr/lib64/jvm/java-1_6_0-ibm-1.6.0/include/ -I/usr/lib64/jvm/java-1_6_0-ibm-1.6.0/include/linux/ -L/usr/lib64/jvm/java-1_6_0-ibm-1.6.0/jre/bin/classic -ljvm -ldl

相關文章